![](/img/trans.png)
[英]Why does ObjectOutputStream.writeObject not take a Serializable?
[英]How to mock objectOutputStream.writeObject()?
我有一个服务器类,故意没有将任何参数传递给它,并且想用Mockito测试它。
如果您想在Github上查看完整的源代码:
Server.class
public class Server extends Thread {
private Other other;
ObjectInputStream fromClient;
ObjectOutputStream toClient;
public Server(){
this.other = new Other(foo, bar);
}
@Override
public void run(){
try{
ServerSocket serverSocket = new ServerSocket(1337);
Socket socket = serverSocket.accept();
fromClient = new ObjectInputStream(socket.getInputStream());
toClient = new ObjectOutputStream(socket.getOutputStream());
while(true) {
int command = (Integer) fromClient.readObject();
switch (command) {
case 0x1:
//add
//...
break;
case 0x2:
//get server data
toClient.writeObject(other.getSomething());
break;
case 0x3:
//delete
//...
break;
default:
break;
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread t = new Server();
t.start();
}
}
问题
我知道Mockito无法模拟最终类,例如ObjectOutputStream和ObjectInputStream。
这正是我遇到问题的地方。
到目前为止,我的测试失败,并在网上显示NullPointerException
when(server.fromClient.readObject()).thenReturn(0x2);
。
当运行最终方法时,这对于Mockito是典型的。
ServerTest.class
@Test
void run() throws IOException, ClassNotFoundException {
//given
Other other = new Other(foo, bar);
Server server = mock(Server.class);
//when
when(server.fromClient.readObject()).thenReturn(0x2);
server.start();
//then
verify(server).toClient.writeObject(other.getSomething());
}
在其他文章中 ,建议可以通过实现接口ObjectInput
更改被测试类的签名来规避最终的问题,因此无论如何都要对其进行模拟。
但是,在提出的方法中,当不将ObjectOutputStream
作为参数传递给被测类时,尚不清楚如何操作。
此外,如我的case
结构所示,当让ObjectInputStream
直接控制ObjectOutputStream
的响应时如何操作,这对于TCP客户端/服务器应用程序来说并非不常见。
到目前为止,我仍然认为我的测试可以工作,而不是ObjectOutputStream
签名中的final关键字。 如果我错了,请在这里纠正我。
问: “为什么不将流传递到服务器?” 答:因为我在其他任何地方都不需要它们。
这样做实际上是最后的努力,如果需要,我会这样做,但我宁愿不这样做。
您可以在Server
类中提供一个从fromClient
输入流读取并模拟它的函数,而不是ObjectInputStream.readObject()
方法:
public class Server extends Thread {
private Other other;
ObjectInputStream fromClient;
ObjectOutputStream toClient;
public Server(){
this.other = new Other(foo, bar);
}
Object readObject() throws ClassNotFoundException, IOException {
return fromClient.readObject();
}
//...
因此您的测试将如下所示:
@Test
void run() throws IOException, ClassNotFoundException {
//given
Other other = new Other(foo, bar);
Server server = mock(Server.class);
//when
when(server.readObject()).thenReturn(0x2);
server.start();
//then
verify(server).toClient.writeObject(other.getSomething());
}
同样的事情也可以应用于toClient
流。
我知道Mockito无法模拟最终类,例如ObjectOutputStream和ObjectInputStream。 >这正是我遇到问题的地方。
到目前为止,我的测试失败,并在网上显示NullPointerException
当(server.fromClient.readObject())。thenReturn(0X2);.
当运行最终方法时,这对于Mockito是典型的。
您的NPE 并非由Mockitos无法模拟final
方法引起。 使用NPE的原因是,当测试代码尝试访问成员变量fromClient
和toClient
它们不会被初始化。
但即使,他们将与实际依赖性的实例初始化,而不是与嘲笑 ,这样会的Mockito无法反正配置它们。
你从根本上误解的目的和技术含义mocks
。 您永远不会嘲笑要测试的课程。 您总是模拟被测类的依赖关系 ,并用此模拟物替换被测代码的实际依赖关系。
在其他文章中,建议可以通过实现接口
ObjectInput
更改被测试类的签名来规避final
问题,因此无论如何都要对其进行模拟。
没有。
建议您针对接口进行编程,而不是针对具体类进行编程,并建议使用依赖注入/控制反转 。
没有人建议您在测试中的类(您的Server
类)实现任何接口。
问:“为什么不将流传递到服务器?”
答:因为我在其他任何地方都不需要它们。这样做实际上是最后的努力,如果需要,我会这样做,但我宁愿不这样做。
我们进行OOP的主要原因之一是代码的可重用性 。
单元测试是最明显的代码重用。 测试您的代码有困难,这表明缺乏可重用性。 在这一点上,您的Server
类根本不打算“重用”并不重要。 这是您的通用编码方法,将来会让您遇到麻烦。
您的Server
类违反了OOP最佳做法:
封装/信息隐藏。
您最有可能将成员变量fromClient
和toClient
包声明为私有 ,以便能够从我们的测试中访问它们。 这有两个方面的问题:
关注点分离
任何类(提供业务逻辑)都应承担单个责任 。 除了业务逻辑之外,实例化依赖项是完全不同的责任。 因此,您的Server
类不应负责执行此实例化。
我知道这有点教条,但是它会导致更好的可测试性和可重用性的代码。 再说一遍: 当前是否不打算重用您的代码并不重要。
恕我直言,您真正的问题是: 如何在不采用OOP最佳实践的情况下测试我的代码?
在这种情况下,您应该使用
PowerMock
明确将您的投降提交至不良设计。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.