Service Doppelgängers can be your friend (do we really need Consumer integration tests?)
What’s the problem?
- I can’t test my application until my third party dependency is running and accessible … doh!
- A test has failed, but I’m not sure if it’s an issue in my application or an issue in a third party dependency … doh!
- I’ve tested with a stub but I need to repeat the test with a real service just to be sure … doh!
To the rescue … Test Doubles aka Stubs
A stub is small piece of code/config that returns a canned response to an API call. Stubs are essential for consumer-service applications development. They allow you to functionally test your code without a dependency on a third party service being available and correct. Furthermore, if they are internally tested by the owning service, they can provide a solid contract, thus minimising the amount of integration testing with real dependencies.
The purpose of stubs is to:
- Support independent development of client-consumer dependencies
- Allow consumers to test scenarios difficult to create naturally
- Enforce a contract
- Support prototyping in consumers
Who should build the stub?
Well … you, of course.
The stub should be authored by the service provider as they are best positioned to keep the stub correct.
What should my stub do?
- Cover all happy path integration scenarios
- Cover all sad paths integration scenarios e.g. 400s, 500s
- Log invocation including inputs and outputs e.g. payloads
- Support assertions made by automated tests against the log
- Parallel any versioned end-point of the real service
Advanced capabilities:
- Have latency to support performance testing
- Statefull
- Provide endpoints for setting up input state for an API
How does the stub differentiate between the input scenarios?
- Use the input to define the response published by the stub
E.g. GET /products/?productType=socks
where ‘socks’ will always returns a 500 response - The stub could provide an endpoint that tells the stub how to respond for the next invocation
E.g. GET /test/products/?response=http500
where the next call to ‘GET /products’ always returns a 500
How do we keep the stub up-to-date and correct?
We need to give confidence to the consumers of the stub that it is honouring the API and that the payloads are correct. We do this by:
- your application having a suite of tests that use the real service and verify the canned stub response against the real service response.
- The tests are run as part of your Continuous Integration (CI) whenever code is merged
This approach bypasses the need for Consumer Driven testing or Pact based testing. Consumers can safely rely on the stub as a true representation of the client contract. All Consumer tests need to do is check that they have invoked the correct Client service. All Clients need to do is invoke a backward compatibility test from the Consumer, which is their normal functional test suite against stubs.
Tool choice for building the stub:
- WireMock
- SwaggerMock
- Et al
Thank you for reading this blog, all feedback is welcome.
For further blogs from the Telegraph:
http://engineering.telegraph.co.uk
-_-