No defined way to test composition of middleware
Problem
Let's say I have a large application, with many middleware. Some middleware are simply composition units of others, for example:
class Middleware::Authenticate
uses Middleware::ExtractBearerToken
uses Middleware::LookupBearerToken
uses Middleware::LogBearerToken
requires :bearer_token
provides :user
def call
provide(user: bearer_token.user)
next_middleware.call
end
end
I think this is a use-case which isn't currently tested well:
- We have boot-time checking of the dependency tree (so we check that
ExtractBearerTokenprovides :bearer_token) - We have unit tests of each sub-middleware behaving in the correct way in isolation.
- We have request specs testing the overall route (including interactions of all the other middleware).
But there doesn't seem to be a simple way of saying, in a test, "run this smaller middleware chain" (i.e. invoke each sub-middleware and then run Middleware::Authenticate#call). The behaviours of this unit might vary depending on inputs/outputs of each sub-middleware in a way which isn't best expressed in isolated unit tests nor in the high-level request specs (which can be slow).
I think that endorsing this kind of testing to uncover interaction edge-cases would be incredibly valuable.
Proposal
Add a high-level API, either to coach/rspec or in the README, for running a full middleware chain like this.
The usage would look something like:
describe Middleware::Authenticate do
subject(:instance) { Coach::Chain.new(described_class) } # this is the interesting bit
it { is_expected.to call_next_middleware }
it { is_expected.to provide(user: ...) }
context "when a bearer token isn't in the request" do
it "bails out before calling the main function"
end
end
I'm aware that we could use Coach::Handler to construct a full request handler object, but that seems like overkill in specs.
I'm assuming we'll need some way to vary the actual input to the call, would it maybe look like
subject(:instance) { Coach::Chain.new(described_class, request: request) }
Maybe?
+1, this bit me HARD