Let’s say you have an app with a structure like this:
(defn some-route-handler [_] (do-stuff))
(defn routes [_]
"/some-route" some-route-handler)
(defn middleware-2 [handler] (fn [req] ...dostuff))
(defn middleware-1 [handler] (fn [req] ...dostuff))
(defn handler [_]
(router routes {:middleware [middleware-1 middleware-2]}))
(http-server handler)
So, it’s pretty generic: app-server, handlers, middleware etc.
Would you align to a testing strategy where you test middleware-1
, middleware-2
and some-route-handler
as unit tests and then maybe setup a “happy path” integration style test which tests requests running through the whole http-server
stack?The reason I raise this is because I often see a mixture of the two and I often find myself wanting to lean towards unit testing most everything instead of creating a ton of integration tests. My theory: as long as all the little bits are tested, and the http server and routing libraries your using are tested, you should be confident that it will all work when pieced together
But then again integration tests also test all the bits, which possibly means less tests overall
If the middleware is simple, I probably wouldn't bother testing it separately. I may or may not test the underlying handler directly. I would definitely have tests of various routes through the whole stack though, because I'd want to ensure I had test coverage for the various documented error cases that the API/site could report back (as well as one or more happy cases).
I'd probably focus on unit-level tests for what the handler itself calls.
I'd agree with this but also I'd say if the middleware is non-trivial you can usually factor it into 2 functions, one that fiddles with the request and another that fiddles with the response, which you can test separately.
Makes sense.
But that comes from a position where I often have a requirements spec for new features that is written at the level of HTTP requests (in some sense) and so I can write tests first for that level of functionality.
(specifically, at work we tend to get fairly solid specs for new REST endpoints for our main APIs and so I use TDD for those "in the large" -- even though, ultimately, those are integration level tests... I implement them using a mostly RDD workflow until I get passing tests, then review and refactor)
So, you’re approach would be different if you had an “evolving spec” + an “unestablished” system?
(i’m assuming your app, from what i’ve gathered, is pretty well defined overall…or maybe “mature” is a better word)
Yeah, how I go about writing tests usually depends on how well-specified the problem is 🙂 I try not to over-constrain my code in terms of tests if I think the behavior is going to change.