简介
我试图通过从一个线程向另一个线程发送字符串来测试套接字连接,其中服务器和客户端套接字是用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
,然后失败的Assert是"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
的mock Socket的引用,因为这行:
when(mockServerSocket.accept()).thenReturn(mockTestClientSocket);
型
下一个是下面的一行:* 注:我相信这就是我的理解和现实的分歧,因为我相信基于调试,这个线程挂在创建这个OutputStreamWriter对象上。我还没有弄清楚为什么。
writer = new OutputStreamWriter(clientSocket.getOutputStream(), DEFAULT_CHARSETNAME);
型
在给定OutputStream
的情况下,创建一个新的OutputStreamWriter
。Mockito知道模拟的客户端套接字的输出流应该是什么样子,因为setup
部分中有以下几行:
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接收)。接下来,我们很好地 Package 了inputStream:
BufferedReader reader = new BufferedReader(new InputStreamReader(mockTestClientSocket.getInputStream(), DEFAULT_CHARSET));
型
通过Mockito的魔力,mockTestClientSocket.getInputStream()
调用实际上返回了之前的iStream
,因为下面这行代码:
when(mockTestClientSocket.getInputStream()).thenReturn(iStream);
型
所以现在我们有一个读取器,它有一个输入流,输入流连接到一个输出流。输出流连接到一个PrintWriter
,也就是println
ing“Hello,world!“s。然而,读取器似乎从来没有得到ready()
。
问题
- 为什么在创建
OutputStreamWriter
的过程中,创建的侦听器线程挂起,以及如何将Hello, World!
字符串从模拟套接字正确发送到模拟客户端?*
1条答案
按热度按时间x7yiwoj41#
我可以通过修改代码中的两件事来让你的单元测试通过:
1.正确模拟
mockServerSocket.accept()
到目前为止,你模仿
mockServerSocket.accept()
太早了,因为mockTestClientSocket
还没有被设置,所以它将返回null
,你需要先设置它,所以你的代码应该是:字符串
2.将线程设置为正确
当你启动一个专用线程来管理你的客户端套接字时,你需要同步你的线程,以确保你的消息可以被读取。
要做到这一点,您可以:
1.通过调用
reader.readLine()
来重写代码,但这是一种阻塞的方法,因为当前线程将等待其他线程写入该行(此处的消息)。型
1.为
TIMEOUT_TIME
设置一个足够大的值,甚至是夸张的大,以确保其他线程将准备就绪,例如,因为它是一个以纳秒为单位的值,您可以将其设置为30
秒,因此可以设置为30_000_000_000L
。如果您没有设置足够大的值,您的测试可能会在缓慢和/或过载和/或共享系统中不稳定,例如用于持续集成的服务器。