gpt4 book ai didi

java - 使用 Mockito 模拟服务器-客户端连接

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:24:18 25 4
gpt4 key购买 nike

简介

我正在尝试通过将字符串从一个线程发送到另一个线程来测试套接字连接,其中服务器和客户端套接字使用 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 的模拟 Socket 的引用,因为这一行:

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);

根据此构造函数的文档,此 “创建了一个 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!”。然而,读者似乎从来没有得到 ready()

问题

为什么创建的监听器线程在创建 OutputStreamWriter 期间挂起,以及我的 Hello, World! 字符串如何从模拟套接字正确发送到模拟套接字客户?

抱歉,我是 Mockito/java.net.* 的新手,总体来说有点厚。我想我已经包含了代码的所有相关部分,但如果有任何不清楚的地方,请告诉我。

最佳答案

我可以通过修改您代码中的两处来使您的单元测试通过:

1。正确模拟 mockServerSocket.accept()

到目前为止,你 mock mockServerSocket.accept() 太早了,因为 mockTestClientSocket 还没有设置,所以它会返回 null,你需要先设置它,这样你的代码应该是:

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。正确同步你的线程

当您启动一个专用线程来管理您的客户端套接字时,您需要同步您的线程以确保您的消息准备好被读取。

为此,您可以:

  1. 通过调用 reader.readLine() 来简化您的代码,但这是一种阻塞方法,因为您的当前线程将等待另一个线程需要的时间写这行(这里的消息)。

代码可以是:

BufferedReader reader = ...

String receivedMessage = reader.readLine();
assertEquals("Hello, world!", receivedMessage);
mockTestClientSocket.close();
connection.closeSocket();
  1. TIMEOUT_TIME 设置一个足够大甚至过大的值以确保其他线程准备就绪,例如,因为它是一个以纳秒为单位的值,您可以将它设置为 30 秒到 30_000_000_000L。如果您没有设置足够大的值,您的测试在缓慢和/或过载和/或共享系统(例如用于持续集成的服务器)中可能会不稳定。

关于java - 使用 Mockito 模拟服务器-客户端连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40414930/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com