A couple of months ago, in Can You Hack It?, I wrote about how I increased the testability of a service by changing it in a way that allowed me to simulate the behaviour of one of its dependencies. With that in place I could force specific code paths to be followed and so explore different scenarios easily.
That was sufficient for a quick and dirty experiment but, because I was changing code, each round was slower than I'd have liked as I had to edit, compile, run, and then test.
--00--
When the next opportunity to work in that area came up, on a different service, I looked for an improvement to my test approach. I realised that I could remove the compile-run step by having some configuration that would specify the response from the external service.
So I taught our product to look for the URL of a downstream service in an environment variable every time it wanted to call it. This gave me very precise control of the outgoing requests which I pointed at a local mock server called httpbun that responds based on the URL of the incoming request.
For example, httpbun.com/404 will return a 404 and httpbun.com/delay/5 forces a delay of 5 seconds before responding. There's a rich set of parameters for specifying other details such as cookies, headers, and response bodies.
That was just what I needed. Now I could run httpbun, run our service, set an environment variable with a URL that simulated the remote scenario I was interested in, send a request to our product, and see how it behaved. It was an extremely productive and tight loop of idea, configure, test, get feedback.
I think of this as "live" mocking.
--00--
I say that was just what I needed and that's true ... until I wanted to explore multi-step scenarios such as
retries. I needed to be able to tell the remote service to respond slowly
the first time a request was made and quickly the second time. Unfortunately,
httpbun's one-shot approach doesn't support that.
So, again, I hacked our product a little. This time, I made it understand that there might be a list of URLs to call in turn for a given session. Again, that was quick and dirty, and enough for what I needed to do at that point.
--00--
I shared what I'd done with the team, and a colleague suggested that Wiremock could probably be used for everything that I'd done with httpbun and deal with the multi-step scenarios too. So we had a quick bake-off session and found that he was right.
At that point, though, I had no more testing to do so I wrote a simple Docker compose file and bash script to help me remember how to set things up and waited for another opportunity.
--00--
When it arrived, the opportunity was on another service with an interesting complexity: for (sigh) reasons only HTTPs communications were allowed by the library our service was using for talking to downstream dependencies.
If you've ever been in this situation you'll know it's a colossal pain because you have to arrange for certificates to be available to both client and server, in the correct formats. It took a little while to work out the correct sequence of steps to enable our service to trust Wiremock as a server ...
Once I'd got that sorted I could look at Wiremock's capabilities in anger. To give a couple of examples: I configured complex scenarios of sequences of requests involving both success and failure responses; I set up an experiment where response latency was distributed evenly around a specific value which I set to be the timeout of our service so that around 50% of requests should be dropped.
--00--
At the same time, I had a colleague who was testing a different integration in the same stack, one where he wanted to live mock the service I was working on. So I showed him the basics of httpbun and Wiremock and he was away and testing too.
--00--
My testing found a bunch of bugs but the repro needed Wiremock so I wrote an internal how-to on setting it up and referenced it from the bug tickets I filed, along with Wiremock JSON config files that showed the issues.
--00--
After some discussion in the team we decided to try to add Wiremock to the test suite for one of our services. A developer used my how-to as a guide to help her set the JUnit runner up, and my config files for the first tests, although for maintainability we didn't use the raw JSON config but instead a mixture of JSON and code.
--00--
After a couple of iterations of adding those kinds of tests we created a helper function that would dump the raw JSON used by any particular test case so that we could re-use it with standalone Wiremock. This is useful for debugging the test or as a starting point for exploratory testing.
We created a README for Wiremock in our test suite and I added some instructions, and my Docker compose file from earlier, to make it easy for anyone to use the dumped JSON files in further testing.
--00--
That might be the end of this story ... or it might not. To be honest, I could have said that at any point above but each time we found a way to build on what had been done to add a little more value.
That's the beauty of iteration, of sharing wins and problems, of collaborating, and of being open to experimentation. This is how I like to work.
Image: https://flic.kr/p/noKRX5
Comments
Post a Comment