【问题标题】:What is the best way to unit test a method using ServerSocket class?使用 ServerSocket 类对方法进行单元测试的最佳方法是什么?
【发布时间】:2013-05-12 20:17:34
【问题描述】:
我有这样的方法:
public boolean isFree(LdapPort port) {
boolean result = false;
try{
ServerSocket ss = new ServerSocket(port.getPortNumber());
ss.getLocalPort();
ss.close();
} catch(IOException ex){
result = true;
}
return result;
}
问题在于,getLocalPort() 在真实系统端口上运行,并且在测试期间它是否通过取决于本地系统。
测试这种方法的有效方法应该是什么?
【问题讨论】:
标签:
java
unit-testing
junit
mocking
serversocket
【解决方案1】:
ServerSocket 实例应该可以通过工厂获得,工厂是 passed as a dependency 给你的班级:
// Passing factory dependency via constructor injection
public PortChecker(IServerSocketFactory socketFactory)
{
this.socketFactory = socketFactory;
}
// ...
ServerSocket ss = this.socketFactory.GetServerSocket(port.getPortNumber());
ss.getLocalPort();
ss.close();
然后,在您的单元测试中,您可以 mock socketFactory 返回 fake 服务器套接字,从而“断开”它与任何现实世界系统的连接。
请注意,ServerSocket 可能还需要是抽象的(例如,由接口/基类表示),因此它也可以被模拟。
【解决方案2】:
使用 PowerMock,您可以模拟构造函数 details。
因此,对您要模拟的类的匹配构造函数的所有调用都可以返回一个模拟实例,您将在测试中准备该实例。
您的测试将需要这些注释:
@RunWith(PowerMockRunner.class)
@PrepareForTest( ServerSocket.class )
然后在测试方法里面:
ServerSocket ssMock = createMock(ServerSocket.class);
expectNew(ServerSocket.class, port.getPortNumber()).andReturn(ssMock); // so constructor of the ServerSocket class taking this specific port number will return the ssMock instance
expect(ssMock.getLocalPort()).andReturn(10); // when method getLocalPort is invoked return 10
reply(ssMock, ServerSocket.class); // do not forget to specify not only the mock instance (ssMock) but the ServerSocket class as well
【解决方案3】:
您需要考虑模拟 serverSocket 类。
使用您当前的代码,新的调用使测试变得困难。
将 serversocket 实例注入你的方法/类
然后你可以注入一个模拟实例进行测试
我一直发现mockito 是一个很好的模拟框架
【解决方案4】:
你可以写
public boolean isFree(LdapPort port) {
try{
new ServerSocket(port.getPortNumber()).close();
return true;
} catch(IOException ex){
return false;
}
}
您可以通过在同一端口上创建一个ServerSocket进行测试,isFree应该为false,然后关闭ServerSocket,isFree应该为true。