Failing Server-Client Connection with Mockito

Introduction

I am trying to test a socket connection by sending a string from one thread to another where the server and client sockets are ridiculed using Mockito v1.9.5.

Here is the test I'm trying to run:

@Test public void testConnection() { //associate a mock server socket with the TCP Connection TcpSocketConnection connection = new TcpSocketConnection(mockServerSocket); try { //begin thread which listens for clients, then sends "Hello, world" to a connected //client. connection.listen(); BufferedReader reader = new BufferedReader( new InputStreamReader(mockTestClientSocket.getInputStream(), DEFAULT_CHARSET) ); long startTime = System.nanoTime(); for (long interval = 0; interval < TIMEOUT_TIME; interval = System.nanoTime() - startTime) { if (reader.ready()) { String receivedMessage = reader.readLine(); assertEquals("Hello, world!", receivedMessage); mockTestClientSocket.close(); connection.closeSocket(); return; } } mockTestClientSocket.close(); connection.closeSocket(); fail("Failed to receive message."); } catch (IOException e) { fail(e.getMessage()); } } 

The test passes until TIMEOUT_TIME , and then the statement that fails is "Failed to receive message." Here I define the behavior of cheated objects:

 @Before public void setup() { mockServerSocket = mock(ServerSocket.class); try { when(mockServerSocket.accept()).thenReturn(mockTestClientSocket); } catch (IOException e) { fail(e.getMessage()); } mockTestClientSocket = mock(Socket.class); try { PipedOutputStream oStream = new PipedOutputStream(); when(mockTestClientSocket.getOutputStream()).thenReturn(oStream); PipedInputStream iStream = new PipedInputStream(oStream); when(mockTestClientSocket.getInputStream()).thenReturn(iStream); when(mockTestClientSocket.isClosed()).thenReturn(false); } catch (IOException e) { fail(e.getMessage()); } } 

Part of what I am trying to verify is the following run() inside the inner class that runs in connection.listen() :

 class InnerListenerClass implements Runnable { @Override public void run() { try { clientSocket = socket.accept(); writer = new OutputStreamWriter( clientSocket.getOutputStream(), DEFAULT_CHARSETNAME); out = new PrintWriter(writer, true); while (!clientSocket.isClosed()) { out.println("Hello, world!"); Thread.sleep(MILLIS_BETWEEN_MESSAGES); } } catch (InterruptedException | IOException e) { LOG.debug(e.getMessage()); } } public InnerListenerClass(final ServerSocket socket) { this.socket = socket; } } 

And here is the part of TcpSocketConnection.java :

 class TcpSocketConnection() { public TcpSocketConnection(final ServerSocket serverSocket) { checkNotNull(serverSocket); this.serverSocket = serverSocket; } ... public final void listen() throws IOException { listenerThread = new Thread(new InnerListenerClass(serverSocket)); listenerThread.start(); } ... } 

How it works in my head

This section is negligible; I will try to go through my test process to add additional context to this question. Starting at the beginning of testConnection() :

 TcpSocketConnection connection = new TcpSocketConnection(mockServerSocket); 

This creates a connection to the mocked ServerSocket associated with it. This creates a stream starting with the following line:

 clientSocket = socket.accept(); 

since socket above is a link to mockServerSocket , Mockito knows to return a link to a Socket layout called mockTestClientSocket because of this line:

 when(mockServerSocket.accept()).thenReturn(mockTestClientSocket); 

The following is the line below: NOTE. I believe that this is where my understanding and reality diverge, I believe, based on debugging, that this stream hangs on the creation of this OutputStreamWriter object. I do not understand why.

 writer = new OutputStreamWriter(clientSocket.getOutputStream(), DEFAULT_CHARSETNAME); 

Create a new OutputStreamWriter considering the OutputStream . Mockito knows what the output stream of a hosted client socket should look like because of these lines in the setup section:

 PipedOutputStream oStream = new PipedOutputStream(); when(mockTestClientSocket.getOutputStream()).thenReturn(oStream); 

In addition, since setup happens before the test, we know that our InputStream refers to this OutputStream because of this line:

 PipedInputStream iStream = new PipedInputStream(oStream); 

According to the documentation for this constructor, "Creates a PipedInputStream" so that it connects to the output stream over the channels (oStream). Then, the data bytes recorded in (oStream) will be available as input from this stream. "

The while loop in run() starts and calls "Hello, world!". to send an OutputStream (and an InputStream is also accepted). Then we gently complete the inputStream:

 BufferedReader reader = new BufferedReader(new InputStreamReader(mockTestClientSocket.getInputStream(), DEFAULT_CHARSET)); 

By magic of Mockito, calling mockTestClientSocket.getInputStream() actually returns iStream from an earlier one due to the following line:

 when(mockTestClientSocket.getInputStream()).thenReturn(iStream); 

So, now we have a reader with an input stream, and this input stream is connected to the output stream. This output stream connects to PrintWriter , which println ing "Hello, world!" S. However, the reader does not seem to even get ready() .

Question

Why OutputStreamWriter created listener stream hang during the creation of the OutputStreamWriter , and how is my string Hello, World! it turns out correctly sent from the mocked socket for the manufactured client?

Apologizes for being Mockito / java.net. * newb and a little thicker overall. I think I have included all the relevant parts of the code, but if something is unclear, let me know.

+6
source share
1 answer

I could make your unit test pass by changing 2 things in your code:

1. Mock mockServerSocket.accept() correctly

Until now, you’re mocking mockServerSocket.accept() too soon, because mockTestClientSocket is not yet set to return null , you need to set it first so your code is faster:

 mockServerSocket = mock(ServerSocket.class); // Set it first mockTestClientSocket = mock(Socket.class); try { // Then mock it when(mockServerSocket.accept()).thenReturn(mockTestClientSocket); } catch (IOException e) { fail(e.getMessage()); } ... 

2. Sync your threads correctly

When you start a dedicated thread to manage your client socket, you need to synchronize the threads to make sure your message is ready to be read.

To do this, you can:

  • Simplify your code by calling reader.readLine() , but this is a blocking approach, as your current thread will wait until it needs another thread to write a line (message here).

The code may be:

 BufferedReader reader = ... String receivedMessage = reader.readLine(); assertEquals("Hello, world!", receivedMessage); mockTestClientSocket.close(); connection.closeSocket(); 
  1. Set the value for TIMEOUT_TIME large enough, even exaggerated, to make sure that another thread is ready, for example, since this value is in nanoseconds, you can set it to 30 seconds to 30_000_000_000L . If you do not set the value large enough, your test may be unstable in a slow and / or overloaded and / or general system, such as the server used for continuous integration.
+2
source

Source: https://habr.com/ru/post/1012006/


All Articles