【发布时间】:2009-09-27 16:18:38
【问题描述】:
我正在编写一个 Java 多线程网络应用程序,并且很难想出一种方法来对从网络客户端发送和接收通信的对象进行单元测试。
对象向多个客户端发送消息,然后等待客户端的响应。
随着每个客户端的响应,仪表板样式的 GUI 会更新。
更详细...
Message 对象表示要发送的文本消息,并包含应接收消息的客户端数组。
Message 对象负责将自己分派给所有适当的客户端。
当对 Message 对象调用 dispatch() 方法时,该对象会为 Client 数组中的每个客户端生成一个新线程 (MessageDispatcher)。
每个 MessageDispatcher:
向客户端打开一个新的 TCP 套接字(Socket)
将消息传递给其客户端... PrintWriter out.println(msg text)
创建一个“状态”对象,该对象被传递到消息对象中的队列,然后传递到 GUI。
每个状态对象代表以下事件之一:
消息传递给 Socket(通过 Printwriter out.println())
显示从客户端收到的回执(通过 BufferedReader/InputStreamReader in.readline()...阻塞直到收到网络输入)
从客户端收到用户确认回执(通过与上述相同的方法)
所以.. 我想对 Message 对象进行单元测试。 (使用 JUnit)
单元测试称为 MessageTest.java(包含在下面)。
我的第一步是设置一个包含单个收件人的 Message 对象。
然后我使用 JMockit 创建了一个模拟 Socket 对象,该对象可以向 PrintWriter 提供一个模拟 OutputStream 对象(我使用的是扩展 OutputStream 的 ByteArrayOutputStream)。
然后,当 MessageDispatcher 调用 (PrintWriter object).out 时,消息文本将理想地传递给我的模拟 Socket 对象(通过模拟 OutputStream),它可以检查消息文本是否正常。
还有 InputStreamReader 的示例原理.... 模拟 Socket 对象还提供了一个模拟 InputStreamReader 对象,该对象提供了一个由 MessageDispatcher 调用的模拟 BufferedReader(如前所述,MessageDispatcher 在 in.readLine() 上阻塞)。此时,模拟 BufferedReader 应该向 MessageDispatcher 提供虚假确认...
// mock Socket
Mockit.redefineMethods(Socket.class, new Object()
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayInputStream input = new ByteArrayInputStream();
public OutputStream getOutputStream()
{
return output;
}
public InputStream getInputStream()
{
return input;
}
});
如果这不是多线程的,这应该可以正常工作。但是我不知道如何使用多个线程来做到这一点。任何人都可以给我任何建议或提示吗?
此外,如果您对设计有任何意见(例如,消息对象负责其自己的交付,而不是单独的交付对象。“依赖注入”风格/每个客户端交付的单独线程),那么我会感兴趣也听到了。
更新:这里是代码:
消息.java
public class Message {
Client[] to;
String contents;
String status;
StatusListener listener;
BlockingQueue<Status> statusQ;
public Message(Client[] to, String contents, StatusListener listener)
{
this.to = to;
this.contents = contents;
this.listener = listener;
}
public void dispatch()
{
try {
// open a new thread for each client
// keep a linked list of socket references so that all threads can be closed
List<Socket> sockets = Collections.synchronizedList(new ArrayList<Socket>());
// initialise the statusQ for threads to report message status
statusQ = new ArrayBlockingQueue<Status>(to.length*3); // max 3 status objects per thread
// dispatch to each client individually and wait for confirmation
for (int i=0; i < to.length; i++) {
System.out.println("Started new thread");
(new Thread(new MessageDispatcher(to[i], contents, sockets, statusQ))).start();
}
// now, monitor queue and empty the queue as it fills up.. (consumer)
while (true) {
listener.updateStatus(statusQ.take());
}
}
catch (Exception e) { e.printStackTrace(); }
}
// one MessageDispatcher per client
private class MessageDispatcher implements Runnable
{
private Client client;
private String contents;
private List<Socket> sockets;
private BlockingQueue<Status> statusQ;
public MessageDispatcher(Client client, String contents, List<Socket> sockets, BlockingQueue<Status> statusQ) {
this.contents = contents;
this.client = client;
this.sockets = sockets;
this.statusQ = statusQ;
}
public void run() {
try {
// open socket to client
Socket sk = new Socket(client.getAddress(), CLIENTPORT);
// add reference to socket to list
synchronized(sockets) {
sockets.add(sk);
}
PrintWriter out = new PrintWriter(sk.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(sk.getInputStream()));
// send message
out.println(contents);
// confirm dispatch
statusQ.add(new Status(client, "DISPATCHED"));
// wait for display receipt
in.readLine();
statusQ.add(new Status(client, "DISPLAYED"));
// wait for read receipt
in.readLine();
statusQ.add(new Status(client, "READ"));
}
catch (Exception e) { e.printStackTrace(); }
}
}
}
....以及相应的单元测试:
MessageTest.java
public class MessageTest extends TestCase {
Message msg;
static final String testContents = "hello there";
public void setUp() {
// mock Socket
Mockit.redefineMethods(Socket.class, new Object()
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayInputStream input = new ByteArrayInputStream();
public OutputStream getOutputStream()
{
return output;
}
public InputStream getInputStream()
{
return input;
}
});
// NB
// some code removed here for simplicity
// which uses JMockit to overrides the Client object and give it a fake hostname and address
Client[] testClient = { new Client() };
msg = new Message(testClient, testContents, this);
}
public void tearDown() {
}
public void testDispatch() {
// dispatch to client
msg.dispatch();
}
}
【问题讨论】:
-
您能否发布一些您正在使用的代码并告诉我们为什么该代码不起作用?
-
只放两个主要对象
标签: java multithreading unit-testing networking junit