【发布时间】:2017-03-17 19:55:51
【问题描述】:
简介
我正在尝试通过将字符串从一个线程发送到另一个线程来测试套接字连接,其中服务器和客户端套接字使用 Mockito v1.9.5 进行模拟。
这是我要运行的测试:
@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());
}
}
测试一直运行到TIMEOUT_TIME,然后失败的断言是"Failed to receive message."
我在这里指定模拟对象的行为:
@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());
}
}
我要测试的部分内容是在connection.listen() 中启动的内部类中的以下run():
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;
}
}
这是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();
}
...
}
它在我的脑海中是如何运作的
我将尝试逐步完成我的测试过程,为这个问题添加一些额外的上下文。从testConnection()开头开始:
TcpSocketConnection connection = new TcpSocketConnection(mockServerSocket);
这将创建一个与与之关联的模拟 ServerSocket 的连接。这将创建一个线程,该线程以以下行开始:
clientSocket = socket.accept();
由于上面的socket 是对mockServerSocket 的引用,因此Mockito 知道返回对名为mockTestClientSocket 的模拟套接字的引用:
when(mockServerSocket.accept()).thenReturn(mockTestClientSocket);
接下来是下面的一行:注意:我相信这是我的理解和现实不同的地方,因为我相信基于调试,这个线程正在创建这个 OutputStreamWriter 对象。我还没弄清楚为什么。
writer = new OutputStreamWriter(clientSocket.getOutputStream(), DEFAULT_CHARSETNAME);
在给定OutputStream 的情况下创建一个新的OutputStreamWriter。由于setup 部分中的这些行,Mockito 知道模拟客户端套接字的输出流应该是什么样子:
PipedOutputStream oStream = new PipedOutputStream();
when(mockTestClientSocket.getOutputStream()).thenReturn(oStream);
另外,因为setup 发生在测试之前,我们知道我们的 InputStream 有一个对这个 OutputStream 的引用,因为这行:
PipedInputStream iStream = new PipedInputStream(oStream);
根据此构造函数的文档,This “创建一个 PipedInputStream,以便将其连接到管道输出流 (oStream)。然后写入 (oStream) 的数据字节将可用作此流的输入。 "
run() 中的 while 循环开始并导致“Hello, world!”被发送到 OutputStream (也被 InputStream 接收)。
接下来,我们将 inputStream 包装好:
BufferedReader reader = new BufferedReader(new InputStreamReader(mockTestClientSocket.getInputStream(), DEFAULT_CHARSET));
借助 Mockito 的魔力,mockTestClientSocket.getInputStream() 调用实际上返回了之前的 iStream,因为以下行:
when(mockTestClientSocket.getInputStream()).thenReturn(iStream);
所以现在我们有了一个带有输入流的阅读器,并且该输入流连接到一个输出流。该输出流连接到PrintWriter,即printlning "Hello,world!"s。然而,读者似乎从来没有得到过ready()。
问题
为什么在创建 OutputStreamWriter 期间创建的侦听器线程挂起,我的 Hello, World! 字符串如何从模拟套接字正确发送到模拟客户端?
很抱歉,我是一个 Mockito/java.net.* newb 并且总体上有点厚。我想我已经包含了所有相关的代码部分,但如果有任何不清楚的地方请告诉我。
【问题讨论】:
-
mockServerSocket = mock(ServerSocket.class);尝试 when(mockServerSocket.accept()).thenReturn(mockTestClientSocket); . . . mockTestClientSocket = mock(Socket.class);.... 在您告诉 Mockito 返回它之后,您正在定义客户端套接字模拟。所以返回的引用不是你所期望的。颠倒这两个并重试。
-
我同意@AshleyFrieze,这是您的主要问题,但如果这仍然没有改变模拟套接字的行为,请尝试其他语法:` doReturn(oStream).when(mockTestClientSocket) .getOutputStream () ;` (注意从方法调用后面移动到点之前的右括号。)在模拟抽象或真实类(而不是接口)时,
when().thenReturn()形式有时会调用真实方法并伪造结果。doReturn().when()表单从不调用真正的方法。 -
这个问题解决了吗?我在问,因为您已将 @nicholas 的答案标记为已接受,但由于赏金仍然开放
-
是的,Nicolas 的回答是我需要让这个测试工作的建议。赏金还有等待期。
标签: java multithreading unit-testing sockets mockito