如何测试内部创建和使用 ServerSocket 的方法
How to test method that internally creates and uses a ServerSocket
我的服务器代码如下所示:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server implements Runnable {
private ServerSocket serverSocket;
public Server(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
@Override
public void run() {
try {
Socket client = serverSocket.accept();
// do stuff
} catch (IOException e) {
e.printStackTrace();
}
}
}
我的计划是编写一个连接到服务器套接字的模拟客户端,并验证结果,但为了做到这一点,我需要知道要连接到哪个端口。但是,此信息是私人信息。
None 个选项我认为是很好的做法:
- 如果我使用预定义的端口号进行测试,我无法保证它一定可用。就算考试前有,理论上也有可能在我尝试使用的时候就抢走了。
- 如果我将 0 作为端口号传递(这样
ServerSocket
将自动提供一个空闲端口),我仍然无法访问它。
- 我可以向接口添加一个
getServerPort()
方法或创建一个接受 ServerSocket
对象的构造函数,但是仅仅为了测试而更改接口被认为是不好的做法。
正如所写,您的 class 并不真正适合单元测试。
问题是您直接调用 new ServerSocket()
基本上剥夺了您 控制 套接字对象将做什么的能力。
那么,你可以做什么:
interface SocketFactory {
public ServerSocket createSocketFor(int port);
}
class SocketFactoryImpl implements SocketFactory {
...
public class Server implements Runnable {
public Server(int port) {
this(port, new SocketFactoryImpl());
}
Server(int port, SocketFactory socketFactory) {
...
换句话说:您使用 依赖注入 来为您的 "class under test" 提供一种手段来创建它需要完成其工作的那些对象。
从那里开始:您可以使用 EasyMock 等模拟框架来 控制 mocked SocketFactory 对象 return(可能是一个 mocked ServerSocket 对象)。现在您可以完全控制生产代码使用的 ServerSocket ...您可以测试任何您想要的情况。
长话短说:不要调用 new;而是使用依赖注入来 完全 控制你的 class 被测试。
(也许观看这些 videos 可以真正理解编写可测试代码的意义)。
我的服务器代码如下所示:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server implements Runnable {
private ServerSocket serverSocket;
public Server(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
@Override
public void run() {
try {
Socket client = serverSocket.accept();
// do stuff
} catch (IOException e) {
e.printStackTrace();
}
}
}
我的计划是编写一个连接到服务器套接字的模拟客户端,并验证结果,但为了做到这一点,我需要知道要连接到哪个端口。但是,此信息是私人信息。
None 个选项我认为是很好的做法:
- 如果我使用预定义的端口号进行测试,我无法保证它一定可用。就算考试前有,理论上也有可能在我尝试使用的时候就抢走了。
- 如果我将 0 作为端口号传递(这样
ServerSocket
将自动提供一个空闲端口),我仍然无法访问它。 - 我可以向接口添加一个
getServerPort()
方法或创建一个接受ServerSocket
对象的构造函数,但是仅仅为了测试而更改接口被认为是不好的做法。
正如所写,您的 class 并不真正适合单元测试。
问题是您直接调用 new ServerSocket()
基本上剥夺了您 控制 套接字对象将做什么的能力。
那么,你可以做什么:
interface SocketFactory {
public ServerSocket createSocketFor(int port);
}
class SocketFactoryImpl implements SocketFactory {
...
public class Server implements Runnable {
public Server(int port) {
this(port, new SocketFactoryImpl());
}
Server(int port, SocketFactory socketFactory) {
...
换句话说:您使用 依赖注入 来为您的 "class under test" 提供一种手段来创建它需要完成其工作的那些对象。
从那里开始:您可以使用 EasyMock 等模拟框架来 控制 mocked SocketFactory 对象 return(可能是一个 mocked ServerSocket 对象)。现在您可以完全控制生产代码使用的 ServerSocket ...您可以测试任何您想要的情况。
长话短说:不要调用 new;而是使用依赖注入来 完全 控制你的 class 被测试。
(也许观看这些 videos 可以真正理解编写可测试代码的意义)。