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.