Aug 082015
 

My last post about mocking a Netty server using Mockito worked for Netty 3.x, but the changes made in Netty 4.0 broke a lot of that work. After spending some quality time reading up on the changes from 3 to 4 and debugging my testing code, I got my mocked Netty server working with Netty 4.0, and now I’m posting it here in the hopes it helps anyone else who’s looking to mock a current Netty server for their unit tests. 

Some notes on upgrading in general

Netty made some pretty major changes in their full version update – we noted some pretty nice speed improvements, and overall, mocking up a version of the server for unit testing wound up slimming down some, although not a lot. The biggest change from 3.x to 4.0 is that the packages for just about everything changed. That’s not too bad, update your imports, and most everything else works just fine.

Setting up your mocked Netty server

Setting up  your test server remains largely the same – you’re just declaring a few less variables. Note the ClientBootstrap and MessageEvent objects are no longer necessary.

import static org.junit.Assert.assertEquals;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.mockito.ArgumentCaptor;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;

public class TestClass {

    private ArgumentCaptor<Object> captor;
    private ChannelFuture success;
    private ChannelHandlerContext context;
    private ChannelPipeline channelPipeline;
    private FullHttpRequest getRequest;
    private FullHttpRequest postRequest;

    @Before
    public void before() {

        success = mock(ChannelFuture.class);
        when(success.isDone()).thenReturn(true);
        when(success.isSuccess()).thenReturn(true);

        getRequest = mock(FullHttpRequest.class);
        postRequest = mock(FullHttpRequest.class);
        
        when(getRequest.getMethod()).thenReturn(HttpMethod.GET);
        when(getRequest.getProtocolVersion())
             .thenReturn(HttpVersion.HTTP_1_1);
        when(getRequest.headers()).thenReturn(new DefaultHttpHeaders());
        when(postRequest.getMethod()).thenReturn(HttpMethod.POST);
        when(postRequest.getProtocolVersion())
            .thenReturn(HttpVersion.HTTP_1_1);
        when(postRequest.headers()).thenReturn(new DefaultHttpHeaders());

    }

    @After
    public void after() {

        captor = null;
        context = null;
        success = null;
        handler = null;
        getRequest = null;
        postRequest = null;

    }
}

So far, this should seem like deja vu. Like I mentioned above, you can now set up your server with a few less object initializations.

Actually testing things.

Testing itself looks very similar, Netty just changed the method you have to call in order to send a message to your server. Now instead of handler.messageReceived(context, event); , the call you need to make is handler.channelRead(context, getRequest); . That’s ultimately the biggest mocking difference between Netty 3 and 4, but it’s also the kind of difference that takes a few hours of stepping through a debugger, looking at autocomplete options, and reading documentation to find if someone doesn’t point it out for you.

An actual test looks something like:

@Test
public void testTheGetThing() {

    when(getRequest.getUri()).thenReturn("/get/endpont/being/tested");
    Map<String, Object> testData = new HashMap<>();
    testData.put("foo", "bar");
    List<Map<String, Object>> expected = new LinkedList<>();
    expected.add(testData);

    handler.channelRead(context, getRequest);

    Object captured = captor.getValue();

    FullHttpResponse response = (FullHttpResponse) captured;
    String data = response.content().toString(CharsetUtil.UTF_8);
    ObjectMapper mapper = new ObjectMapper();
    
    List<Map<String, Object>> actual = mapper.readValue(data.getBytes(),
        new TypeReference<List<Map<String, String>>>() {});

    assertEquals(expected, actual);
}

@Test
public void testThePostThing() {

        when(postRequest.getUri()).thenReturn("/post/endpoint/being/tested");

    StringBuilder params = new StringBuilder();
    params
        .append("paramName1=stringValue1")
        .append("paramName2=stringValue2");
    ByteBuf buffer = Unpooled.wrappedBuf(params.toString().getBytes());
    HttpHeaders.addHeader(postRequest, HttpHeaders.CONTENT_LENGTH, buffer.readableBytes());
    HttpHeaders.addHeader(postRequest, HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
    when(postRequest.content()).thenReturn(buffer);

    Map<String, Object> expected = new HashMap<>();
    expected.put("foo", "bar");

    handler.channelRead(context, postRequest);

    Object captured = captor.getValue();

    FullHttpResponse response = (FullHttpResponse) captured;
    String data = response.content().toString(CharsetUtil.UTF_8);
    ObjectMapper mapper = new ObjectMapper();

    List<Map<String, Object>> actual = mapper.readValue(data.getBytes(), new TypeReference<List<Map<String, Object>>>() {});

    assertEquals(expected, actual);

}

As you can see, setting up the call to our Netty server is easier than before, we just have to mock the URI being tested so it gets routed to the appropriate block of handler code. The actual call to send the request has been changed, which is another change that’s easier to make once someone points out the difference to you. The biggest difference here is  captured changing from an HttpResponse to FullHttpResponse. That’s because HttpResponse, doesn’t include the actual content returned, which is bad if you’re trying to validate say…your JSON responses. So, FullHttpResponse it is.

That’s the updated setup for mocking an updated Netty server using Mockito. All in all, it’s pretty straightforward, and less verbose than previous versions of Netty, which is great. There were a few points that took some concerted digging into Netty documentation, but all in this was a pretty easy update.

In other news

In the previous post about mocking a Netty server, I had mentioned wanting to generalize this into an object that we could use in other tests. I’m pretty happy to report that I abstracted a mocked Netty server into its own object. The pull request hasn’t been approved, and it’s pretty specific to our stuff. Since it’s not going to be going into 1 of our open source projects, I don’t have any code to show here, but the process is pretty straight-forward.

If you’re using the latest Netty server in your code, I hope this post helps you get unit tests up and running for your project. If you’re not using Netty, I hope this encourages you to find ways to spin up or mock an embedded server for your own web services. This process isn’t too bad, it took me a couple of days and some heavy Googling, and I’m sure whatever your setup it can be tested the same way. And while it is possible to structure your code in a manner where you’re testing everything but the web calls themselves, testing the calls in a manner closest to how they’re actually getting made in production is the ideal solution. Happy coding and good testing everybody.

 Posted by at 12:04 PM