Mocking HTTP Servers
11 Jun 2011The problem
There are tests (mostly what we call acceptance tests). The system under test (SUT) works with a couple of web services to do its work. The problem I’m faced with is that, when I write these tests, the external web services have to be arranged with data for the test, or the test has to rely on existing data. Writing these tests is extremely painful. Knowledge of the magic existing data is required and in the end what we are really writing are integration tests. But we don’t want integration tests.
At 7digital, we are exposing more of our domain via HTTP APIs, in a “NotSOA” manner. To test each service by itself it becomes necessary to mock the dependencies.
Solutions.
There are a couple of solutions to this.
Set up an stub HTTP web service somewhere, and let it return canned responses. It behaves like the real web service, but only returns responses that have been arranged. The disadvantage of this approach is that I have to know about what canned responses have already been setup.
To change the response for a particular test I have to make changes to the stub server and deploy it, as it is a separate application. It takes the focus away from writing the test I’m concerned with.
Another way is to insert some sort of “switch” in production code that will return canned responses when under test. I don’t like this approach because it requires production code just for tests
My solution.
What I want to do is something similar to setting up mocks/stubs in unit tests, but to do it with an actual http server. To setup the stubbed responses in the test code itself, and not to have to make any change to production code, other than a configuration change.
So this is what I came up with
1: [Test]
2: public void SUT_should_return_stubbed_response() {
3: IStubHttp stubHttp = HttpMockRepository
4: .At("http://localhost:8080/someapp");
5:
6:
7: const string expected = "<xml><>response>Hello World</response></xml>";
8: stubHttp.Stub(x => x.Get("/someendpoint"))
9: .Return(expected)
10: .OK();
11:
12: string result = new SystemUnderTest().GetData();
13:
14: Assert.That(result, Is.EqualTo(expected));
15:
16: }
HttpMockRepoisitory.At creates a HTTP server listening on port 8080, and behaves as if it is process request under the /someapp path. This is the web service that the SUT will get it’s data from.
Using the object returned, it is possible to setup stubbed responses using a fluent syntax. The stub server can return text and files. I’ve posted a few more examples on github http://github.com/hibri/HttpMock/blob/master/src/HttpMock.Integration.Tests/HttpEndPointTests.cs
Kayak.
I’m using Kayak, a light weight, asynchronous HTTP server written in C#. Kayak, can accept request processing delegates, and post them to the HTTP server listening on the given socket. This allows me to add stub responses at runtime.
Current status.
This is very much a work in progress. HTTP GET works. There is support for returning stubbing content types and HTTP return codes. I’ll be able to add to this while changing a very large test suite to not rely on real web services. I’ve created a repository on github at http://github.com/hibri/HttpMock
There are no unit tests now, but I’ll be adding them soon as I wanted to prove the concept first.
Describing this as mocking is not entirely correct, but I couldn’t find a term to describe the concept. It is possible to do the same in Ruby using Sinatra.