Writing and running our first test

We're going to create a few tests for the validators we created in Chapter 5, Handling Form Input. In our directory structure, we have the src and test directories. I'll give you two seconds to determine under which directory our tests should go… got it? If you said src, then I have failed to achieve the modest task that was my charge. We will be storing our tests in the test directory using the following steps:

  1. Create a directory in the /test/hisptr/test directory called validators.
  2. Create a file in the /text/hipstr/test/validators directory called user_validator_test.clj.
  3. In user_validator_test.clj, define our namespace and include the clojure.test namespace and the namespace we wish to test—in our case, the hipstr.validators.user-validator namespace:
    (ns hipstr.test.validators.user-validator-test
      (:require [hipstr.validators.user-validator :as uv])
      (:use clojure.test))
  4. Next, let's add a test that ensures only one error message is returned in the errors map when an e-mail address is blank:
    (deftest only-1-error-message-returned-when-email-is-blank
      (let [result (:email (uv/email-validator {:email ""}))]
        (is (= 1 (count result)))))

Save the file, open the terminal, and we'll run the test.

Running tests

Running tests is dead easy using the Leiningen test task. The lein test task, when run by itself, will run all tests under the hipstr.test namespace. Alternatively, we can provide one or more namespaces that we wish to isolate test execution to, which, for now, we will use. Open your terminal, navigate to the root of your source tree, and tell Leiningen to run all the tests in the hipstr.test.validators.user-validator-test namespace:

Running tests

The output indicates that it ran one test containing a single assertion, and that it passed. This is great, as this is the test and assertion that we wrote. Manually running tests can become cumbersome over time. Thankfully, there are plugins to automate this for us.

Running tests automatically

While using lein test gets the job done, it has some drawbacks, most notably you have to manually run the tests (which becomes annoying) and it has a slow start up time. Instead of being a prisoner of our own development, we can break the shackles of keystrokes using the lein quickie plugin.

The lein quickie plugin will watch our code, and anytime it detects a change (save), all the tests in our classpath whose namespaces include our project name and end in test, will be re-evaluated. It even produces a green or red bar indicating whether our tests successfully passed or failed.

To install and use the lein quickie plugin, perform the following steps:

  1. Add the plugin to the project.clj file's :plugins list, as follows:
    :plugins [[lein-ring "0.8.13"]
      [lein-environ "1.0.0"]
      [lein-ancient "0.5.5"]
      [quickie "0.3.6"]]
  2. In the terminal, refresh your dependencies by doing a lein deps.
  3. Still in the terminal, run lein quickie. The plugin will scan for all your tests, run them, and start watching for changes.
    Running tests automatically

If you make a quick change to the user-validators-test namespace (say, insert a space and hit save), the lein quickie plugin will automatically detect the change and re-run the tests.

Running tests automatically

Finally, you can quit lein quickie session at anytime by hitting Ctrl + C.

Note

There are more options you can incorporate to run tests with lein quickie, such as limiting the scope of which tests are validated. Take a look at the lein quickie docs at https://github.com/jakepearson/quickie.

Refactoring tests

Let's write another test, which ensures that the sole message returned indicates that the e-mail is a required field. Full caveat: I don't typically like writing tests, which test copy (content) because copy tends to change almost every day. Testing for copy is one of the biggest testing pain points I've seen on teams over the years, and I don't believe that its value outweighs manual testing (in fact, I think it has an overall negative value due to the maintenance overhead). However, because our e-mail validator can return different messages depending on what went wrong, we should ensure that it returns the correct message. Add the following test, which ensures that the appropriate message is returned when an e-mail is not provided:

(deftest blank-email-returns-email-is-required-message
  (let [result (:email (uv/email-validator {:email ""}))]
    (is (= "is a required field" (first result)))))

If you save and run this test (or just save, if you're using lein quickie), you'll see that it passes. That's great. What isn't so great is that we have the same code written in each test, and code we'll likely need to use for subsequent e-mail validation tests, that sets up the result value. Let's create a simple function that validates an e-mail address for us, and then returns the :email errors in the validator result map, as follows:

(defn validate-email [email]
  "Validates the provided email for us, and returns the
   set of validation messages for the email, if any."
  (:email (email-validator {:email email})))

With the validate-email function, we can now change the let form in each of our tests to be as follows:

(let [result (validate-email "")] )

The test is now a bit easier to read. We can do one more refactoring before moving on. Because the two tests are so similar (we are only expecting one message back, and that message is expected to have a certain value), we can move the two assertions into the same test. Move the assertion in only-1-error-message-returned-when-email-is-blank into blank-email-returns-email-is-required-message, and then delete the only-1-error test:

(deftest blank-email-returns-email-is-required-message
  (let [result (validate-email "")]
    (is (= 1 (count result)))
    (is (= "is a required field" (first result)))))

While we typically want to keep our tests focused and avoid issuing a lot of assertions, moving the two assertions into a single deftest, in this case, works because asserting a single error message exists is a precursor to asserting the actual message itself.

At this point, you should have the hang of it. There's no real magic to writing unit tests in Clojure. We will leave the validator tests for now and move on to how we can do integration tests using clojure.test and Ring.

Tip

View the entire hipstr.test.validators.user-validator-test namespace at http://bit.ly/11FI8PX, as well as its impact on refactoring the hipstr.validators.user-validator namespace at http://bit.ly/1Cvui3n.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.75.70