Writing a high-level integration test

I've always been weary of writing integration tests in the manner of unit tests because when they fail they're difficult to diagnose. All that a failed unit-style integration test tells us is that something, somewhere, broke down. In an ideal world, you would also have a unit test, which when an integration test fails, will also fail. However, rare is the team filled with unit test passion such that a beautiful pairing of failures exist. So, while we will see how we can use James Reeves' ring-mock to do a full-fledged integration test, we must also keep in mind that integration tests on their own are not enough.

For this example, we are going to add an additional test to the hipstr.test.handler namespace that was generated by Luminus when we generated our project. You'll see something like this:

(ns hipstr.test.handler
  (:use clojure.test
           ring.mock.request
           hipstr.handler))

(deftest test-app
  (testing "main route"
    (let [response (app (request :get "/"))]
      (is (= 200 (:status response)))))

  (testing "not-found route"
    (let [response (app (request :get "/invalid"))]
      (is (= 404 (:status response))))))

We've not executed any of the tests from this namespace yet, but if you recall, simply running the lein test, without specifying any namespaces will run all tests under the hipstr.test namespace. Alternatively, if you change the namespace to end in test, the lein quickie plugin will also execute it.

Using ring.mock.request

The only real difference between how we construct the integration tests and the unit tests is that we use the ring.mock.request function. This function will actually construct a valid request map for the given HTTP method and URI, and any parameters we want to provide to the endpoint. Afterwards, the ring.mock.request function runs that request map through our stack, executing everything along the matching route handler.

In that spirit, we can test and ensure that our /signup POST route, will not return a 302 redirect to the /signup-success GET route unless all of the parameters (:email, :username, and :password) are valid. We'll construct another test context, and create the first assertion—that a missing e-mail returns a 200 response OK instead of a 302 redirect. Use following lines of code to do this:

(deftest missing-email-address-redisplays-the-form
  (let [response (app (request :post "/signup"
    {:username "TheDude" :password "123456789"}))]
    (is (= 200 (:status response)))))

This test will actually invoke our /signup POST route in our home-routes, and drill all the way through the plumbing, including rendering the response. Note that integration tests will also interact with any external, dependent systems, such as the database. As such, it's important that you pay pay close attention to what your integration test interacts with; if it mutates any data, you must reset that data prior to running the next test. For this reason, we will not test for a successful 302 redirect because this would ultimately pollute our database.

Note

Unit testing is an academic pursuit well beyond the means of this book. If you are interested in getting deep into unit testing, I highly recommend that you read Gerard Meszaros' xUnit Test Patterns, a book with an incredible wealth of knowledge when it comes to writing sustainable automated tests.

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

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