Unit testing ChannelHandlers

"이희승 (Trustin Lee)" trustin at gmail.com
Thu Aug 6 02:12:52 EDT 2009


Hi Iain,

Because Channel or other interfaces have many operations and hold
somewhat complicated state, it will not be easy to implement a mock with
jMock or EasyMock.  Instead, I'd recommend to extending an 'Abstract*'
classes in Netty.  You might want to refer to the codec embedder source
code, which simplifies the unit testing of codec handlers.

HTH,
Trustin

On 07/28/2009 05:42 AM, Iain McGinniss wrote:
> Hi guys,
> 
> I've been trying to implement the binary protocol used in JXTA using
> Netty for the past week or so (mixed in with trying to implement an HTTP
> tunnel that doesn't use servlets, but that's parked for the moment). In
> doing this I've been trying to unit test my channel handlers, which I
> have found to be quite challenging. I'm starting to wonder whether this
> is because I am taking the wrong approach. First, I'll give a brief
> overview of the protocol:
> 
> 1. When a connection is opened, I must send a "welcome" message. This
> contains some dynamic data used to identify myself, such as a unique ID,
> the version of the protocol I am using and some meta-data about how the
> connection should be used.
> 2. While this is happening, the peer on the other end will be sending
> their welcome message. This message ends with CRLF.
> 3. If I don't receive a full welcome message from the other peer within
> 5 seconds, or if it is invalid in some way (wrong protocol version,
> unsupported modes specified in the meta-data, etc), then I must close
> the connection.
> 4. I cannot send any messages to the other peer until I have received a
> full, valid welcome message from them.
> 5. Once this welcome message handshake is complete, I can send messages
> to the peer. These messages are structured somewhat like HTTP requests,
> except in binary with known binary keys mapping to known binary values.
> One of these "headers" specifies the length of the rest of the message,
> so as a receiver I can predict how long the message is, preallocate a
> buffer of the correct size, read the message and then process it.
> 6. Either peer can close the connection at the end of any message,
> without any kind of "goodbye" message required.
> 
> In order to deal with this protocol I've created a ChannelHandler,
> extending SimpleChannelHandler.
> 
> 1. I override the channelConnected() method in order to send my welcome
> message, and I do not allow the connected event to bubble up the
> pipeline at this point. 
> 2. I override the messageReceived() method to process the welcome
> message, and call channel.close() within this if the welcome message is
> invalid or not what I expect. Once I receive a full welcome message, I
> fire my own UpstreamChannelStateEvent to state we are connected
> (properly). I actually don't know if this is "legal" or best practice.
> 3. I override the writeRequested() method to perform the framing of the
> ChannelBuffers containing full messages passed down from the previous
> handler which has done the serialization.
> 
> Now, unit testing this has proved challenging. For instance, take the
> simple unit test of "when channelConnected() is called, write a welcome
> message to the channel". The implementation of channelConnected is:
> 
> public void channelConnected(ChannelHandlerContext ctx,
> ChannelStateEvent e) throws Exception {
>     SocketAddress remoteAddress = ctx.getChannel().getRemoteAddress();
>     EndpointAddress destAddr = new
> EndpointAddress(endpointAddr.getProtocolName(),
> remoteAddress.toString(), null, null);
>     WelcomeMessage welcome = new WelcomeMessage(destAddr, endpointAddr,
> peerId, false);
>     ChannelBuffer welcomeBytes =
> ChannelBuffers.copiedBuffer(welcome.getByteBuffer());
>     Channels.write(ctx, Channels.future(ctx.getChannel()), welcomeBytes);
> }
> 
> My approach so far to unit testing this has been to use jMock 2,
> producing tests blocks like this:
> 
> @Test
> public void testSendsWelcomeMessageImmediately() throws Exception {
>     final UpstreamChannelStateEvent ev = new
> UpstreamChannelStateEvent(channel, ChannelState.CONNECTED, true);
>     final WelcomeMessage expectedWelcomeMessage = new
> WelcomeMessage(REMOTE_ENDPOINT_ADDR, LOCAL_ENDPOINT_ADDR, LOCAL_PEER_ID,
> false);
>         
>     mockContext.checking(new Expectations() {{
>         ignoring(ctx).getChannel(); will(returnValue(channel));
>         atLeast(1).of(channel).getRemoteAddress();
> will(returnValue(REMOTE_SOCK_ADDR));
>       
>  one(ctx).sendDownstream(with(aDownstreamMessageEvent(aWelcomeMessage(expectedWelcomeMessage))));
>     }});
>         
>     handler.channelConnected(ctx, ev);
> }
> 
> The real sticking point was the "aDownstreamMessageEvent" and
> "aWelcomeMessage" Matchers I had to write by hand. The various event
> types in Netty do not implement equals() so I cannot do something like:
> 
> DownstreamMessageEvent expectedEvent = new DownstreamMessageEvent(...);
> one(ctx).sendDownstream(with(equals(expectedDownStreamMessage));
> 
> Additionally, in other test cases where I want to assert that my code
> fires events like an UpstreamChannelStateEvent with State=CONNECTED,
> Value=TRUE, I can't do this using equals() either. Instead, I must write
> custom matchers to compensate for this. All this results in, for a
> simple method like channelConnected(), roughly 5-6 times the amount of
> code required to write a test compared to the code itself (aggravated by
> most aspects of JXTA being difficult to test too, and using NIO
> ByteBuffers rather than ChannelBuffers... sigh). This will get easier
> over time as I start building more matchers for jmock specifically for
> Netty, but I still can't help feeling that there must be an easier way
> to test ChannelHandlers in isolation.
> 
> Do you guys have any advice on how best to test Netty code?
> Iain
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> netty-users mailing list
> netty-users at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/netty-users



More information about the netty-users mailing list