When a Java Class Under Test makes HTTP calls that are tricky to mock, the Simple framework offers a neat alternative for a self-contained stub.
Mocking is a great technique for isolating a class under test from its dependencies and simulating its interactions. With the plethora of innovative open source mocking frameworks available today, all but the knottiest of test scenarios can be covered this way.
Mocks don’t work so well when a class under test interacts with an external resource such as a message queue or web server. Sure, we can mock the library calls that the class makes, but aside from sometimes being quite painful, this essentially tests the code pattern rather than the expected results. It also ties our test to the choice of library and potentially even the version of it which the developer used. This neutralises a key benefit of good test coverage – the ability to refactor our code and prove our new implementation against a proven, unchanged set of test cases. It also means our “test description” is describing the wrong thing.
A black-box approach can work better in these situations, stubbing the resource we interact with rather than mocking the calls that the Class Under Test makes. Stubbing of course brings its own challenges, embedding a stubbed resource in our test code in such a way that it is reliable and repeatable whether we’re running it on a development PC or on a shared continuous build server can make for a fair bit of hoop jumping. Yes, we’re testing the results of the code rather than the code itself, but our “test description” is still corrupted by all that set up and teardown logic.
Fortunately there are some really lightweight server implementations out there that can be embedded in a test with minimal effort. If you need to embed an HTTP server to support a Class Under Test, the Simple Framework (http://www.simpleframework.org/) offers a really neat solution.
The problem
Keen to cut down on negative publicity, the management team at Value European Ready Meals Inc. (Vermin) have decided to keep an eye on the use of certain “sensitive” stock items. The MIS developer has provided a URL to be called by the warehouse system whenever these sensitive items are drawn from stock.
Stock orders are processed through a Spring configured chain of handlers which implement the StockMovementHandler interface:
public interface StockMovementHandler { public boolean handle(StockMovement stockMovement) throws StockMovementException; }
A developer will provide a new implementation class StockItemMovementNotifier exposing properties to configure the URL to call and the list of sensitive terms to be notified:-
public class StockItemMovementNotifier implements StockMovementHandler { public boolean handle(StockMovement stockMovement) } public void setItemsToNotify(String[] itemsToNotify) { } public String[] getItemsToNotify() { } public void setUrlToNotify(String urlToNotify) { } public String getUrlToNotify() { } }
We need to provide a test for this class. We could crack open the implementation and mock the library calls but this ties us to the library used and the code pattern followed. It also doesn’t adequately confirm scenarios like the HTTP server not being available.
Stubbing would definitely be better.
The solution
And fortunately, with the Simple framework – like it says on the tin – it’s pretty simple. First of all we need to add it to our Maven dependencies (or add simple.jar to our classpath).
<dependency> <groupId>org.simpleframework</groupId> <artifactId>simple</artifactId> <version>5.0.4</version> <scope>test</scope> </dependency>
We provide the logic for a Simple server stub by implementing the Container interface (org.simpleframework.http.core.Container) which looks like this:-
public interface Container { void handle(Request req, Response resp); }
The Request object has a number of methods to help us interrogate the request details and the Response object lets us configure the response sent back. Interestingly all traffic to the stubbed server’s port gets picked up by the same Container object; req.getTarget() lets us control code execution based on the target URI.
For our test we’ll want to confirm the correct URI is being hit by our Class Under Test and that the correct parameter values are being posted. Plus we’ll want to be able to configure an HTTP response code to be sent back to it.
Here’s our ServerStub nested class which handles all this:-
private static class ServerStub implements Container { private int responseCode = 0; private String uri = null; private String itemCode = null; private String quantity = null; private String method = null; public void handle(Request req, Response resp) { try { method = req.getMethod(); uri = req.getTarget(); quantity = req.getParameter("quantity"); itemCode = req.getParameter("itemCode"); resp.setCode(responseCode); resp.close(); } catch(IOException eIo) { throw new RuntimeException(eIo); } } public void setResponseCode(int responseCode) { this.responseCode = responseCode; } public String getUri() { return uri; } public String getItemCode() { return itemCode; } public String getQuantity() { return quantity; } public String getMethod() { return method; } }
We’ve provided getters for the request attributes we’ll want to interrogate, and a setter for the response attributes we’ll want to set.
Our handle() method extracts the relevant request attributes, sets the response code and then calls resp.close(). If we don’t call this method we’ll get no response back from the stubbed server. Note that unlike the other methods it throws an IOException, though the interface for handle() doesn’t support throwing exceptions on. Here we’re just wrapping it in a RuntimeException.
Setup and Teardown
Before our test method runs we’ll need to start the stubbed server. Our startServer() method creates a new ContainerServer instance (org.simpleframework.http.core.ContainerServer) wrapping our stub Container object. It then creates a new SocketConnection (org.simpleframework.transport.connect.SocketConnection) to attach it to a socket. Finally it provides a java.net.InetSocketAddress object to connect it to.
private Server server = null; private SocketConnection connection = null; private SocketAddress socketAddress = null; private ServerStub stub = null; @Before public void startServer() throws IOException { stub = new ServerStub(); server = new ContainerServer(stub); connection = new SocketConnection(server); socketAddress = connection.connect( new InetSocketAddress(0)); }
Since we’re providing a port number of 0 we’ll be allocated any free socket on the machine running the test. The SocketAddress object returned by the connect() method lets us find that port number so we can call it in our test methods. Since we’ll always find ourselves a free port to use we can reliably run this on any machine without worrying about conflicts with other services. We’ll keep a reference to the returned SocketAddress as it’ll be useful later. We also keep references to our ContainerServer and SocketConnection so we can clean them up after the test runs:-
@After public void stopServer() throws IOException { connection.close(); server.stop(); }
The tests
Now we’re ready to start writing test methods. We’ll kick off with a positive test case, where the Class Under Test should call the remote URL for the supplied stock movement and the server returns an HTTP 200 OK code:-
@Test public void testSuccessfulPost() throws StockMovementException { final String uri = "/notifyMovement.php"; final long quantity = -50; final String itemCode = "Horse Carcass"; final String[] itemCodes = new String[]{"Horse Carcass", "Rat Carcass"}; StockItemMovementNotifier notifier = new StockItemMovementNotifier(); notifier.setItemsToNotify(itemCodes); notifier.setUrlToNotify("http:/" + socketAddress + uri); stub.setResponseCode(HttpStatus.SC_OK); StockMovement movement = new StockMovementImpl(itemCode, quantity); notifier.handle(movement); Assert.assertEquals(uri, stub.getUri()); Assert.assertEquals( Long.toString(0 - quantity), stub.getQuantity()); Assert.assertEquals("POST", stub.getMethod()); Assert.assertEquals(itemCode, stub.getItemCode()); }
Our test method does nothing more than what we’d want – configure the Class Under Test, configure our test data, invoke the method we want to test and verify the results.
When configuring the URL for the Class Under Test to call, we use the SocketAddress object obtained in our startServer() method to supply the address and port number our stubbed server is running on.
As this basic example shows, the Simple framework provides a great solution to providing a code-light, reliable and repeatable stub for a web server that is, er, really simple.