Introduction
I'm attempting to test a socket connection by sending a string from one thread to another, where server and client sockets are mocked with Mockito v1.9.5.
Here's 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 runs until TIMEOUT_TIME
and then the assertion that fails is the "Failed to receive message."
I specify the behavior of the mocked objects here:
@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'm trying to test is the following run()
within an inner class which is kicked off in the 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's a portion of the 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
I'm going to attempt to step through the process of my test to add some extra context to this question. Starting at the beginning of testConnection()
:
TcpSocketConnection connection = new TcpSocketConnection(mockServerSocket);
This creates a connection with a mocked ServerSocket associated with it. This creates a thread which kicks off with the following line:
clientSocket = socket.accept();
since socket
above is a reference to mockServerSocket
, Mockito knows to return a reference to a mock Socket called mockTestClientSocket
because of this line:
when(mockServerSocket.accept()).thenReturn(mockTestClientSocket);
Next is the line below: NOTE: I believe this is where my understanding and reality diverge, as I believe based on debugging that this thread is hanging on creating this OutputStreamWriter object. I haven't figured out why.
writer = new OutputStreamWriter(clientSocket.getOutputStream(), DEFAULT_CHARSETNAME);
Make a new OutputStreamWriter
given an OutputStream
. Mockito knows what the output stream of the mocked client socket should look like because of these lines in the setup
section:
PipedOutputStream oStream = new PipedOutputStream();
when(mockTestClientSocket.getOutputStream()).thenReturn(oStream);
Also, because the setup
happens before the test, we know that our InputStream has a reference to this OutputStream because of this line:
PipedInputStream iStream = new PipedInputStream(oStream);
According to the documentation for this constructor, This "Creates a PipedInputStream so that it is connected to the piped output stream (oStream). Data bytes written to (oStream) will then be available as input from this stream."
The while loop in run()
begins and causes "Hello, world!" to be sent out the OutputStream (and also received by the InputStream).
Next, we wrap up the inputStream nicely:
BufferedReader reader = new BufferedReader(new InputStreamReader(mockTestClientSocket.getInputStream(), DEFAULT_CHARSET));
By the magic of Mockito, the mockTestClientSocket.getInputStream()
call actually returns the iStream
from earlier, because of the following line:
when(mockTestClientSocket.getInputStream()).thenReturn(iStream);
So now we have a reader with an input stream, and that input stream is hooked up to an output stream. That output stream is hooked up to a PrintWriter
which is println
ing "Hello,world!"s. However, the reader never seems to even get ready()
.
Question
Why is the created listener thread hanging during creation of an OutputStreamWriter
, and how can my Hello, World!
string get sent properly from mocked socket to mocked client?
Apologies for being a Mockito/java.net.* newb and being a bit thick in general. I think I've included all the relevant portions of code, but if anything's unclear let me know.