我们如何编写 Socket 连接的测试用例
How can we write test cases for Socket connections
下面是需要测试Socket连接真假场景的代码片段。
public boolean pingHost(String hostname, int port) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(hostname, port), 3000);
return true;
} catch (IOException e) {
return false;
}
}
这是模拟的标准用例。使用 PowerMock,即您可以模拟构造函数调用和 return 模拟对象。您还可以在构造函数调用中验证正确的参数。只需搜索 'Powermock mocking constructor'。
如果模拟不是您的选择,另一种方法是使用可用于 *NIX 和 Windows 平台的 netcat 在端口上侦听。
编辑 2021.12.16 ----------------
您需要包含这四个 jar 才能使用 Mockito 和 PowerMock (Maven pom)
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
<!-- https://www.baeldung.com/intro-to-powermock -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
但是有很多关于如何设置 Mockito 和 PowerMock 的文档。
这是一个带有一些注释的可运行示例,让您大致了解如何使用模拟进行测试。
package __scratchpad__;
import static org.junit.Assert.fail;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(PingerTest.Pinger.class)
public class PingerTest {
// Inner class just to have a compact 'all in one' example
public class Pinger {
// refactored to better coding style (avoid 'Magic numbers')
// AND for testability because TIM-OUT is now accessible for test
public static final int TIME_OUT = 3000;
public boolean pingHost(String hostname, int port) {
try (Socket socket = new Socket()){
socket.connect(new InetSocketAddress(hostname, port), TIME_OUT);
return true;
} catch (IOException e) {
return false;
}
}
}
@Test
public void testPingHost() {
// create required mock objects to inject
InetSocketAddress addressMock = Mockito.mock(InetSocketAddress.class);
Socket socketMock = Mockito.mock(Socket.class);
// create parameters
final String hostname = "answer.deepthought";
final int port = 42;
final int timeOut = PingerTest.Pinger.TIME_OUT;
// create class under test
Pinger pingerUnderTest = new Pinger();
try {
// mock constructor call of class InetSocketAddress and verify correct parameters
// If parameters of call are different to the expected, than NO mock object is returned, the test will fail
// Otherwise the addressMock object is returned
whenNew(InetSocketAddress.class)
.withArguments(hostname, port)
.thenReturn(addressMock);
// mock constructor call of Socket class
whenNew(Socket.class)
.withNoArguments()
.thenReturn(socketMock);
// call method under test with parameters defined earlier.
// If parameters are different to them of InetSocketAddress constructor call the test will fail.
pingerUnderTest.pingHost(hostname, port);
// if all works fine exactly one call to method connect() with specified parameters MUST appear
// if there are more or less calls or any of the parameters is different, than the test will fail
Mockito.verify(socketMock).connect(addressMock, timeOut);
} catch(Exception e) {
e.printStackTrace();
fail("Unexpected exception caught!");
}
}
}
下面是需要测试Socket连接真假场景的代码片段。
public boolean pingHost(String hostname, int port) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(hostname, port), 3000);
return true;
} catch (IOException e) {
return false;
}
}
这是模拟的标准用例。使用 PowerMock,即您可以模拟构造函数调用和 return 模拟对象。您还可以在构造函数调用中验证正确的参数。只需搜索 'Powermock mocking constructor'。 如果模拟不是您的选择,另一种方法是使用可用于 *NIX 和 Windows 平台的 netcat 在端口上侦听。
编辑 2021.12.16 ----------------
您需要包含这四个 jar 才能使用 Mockito 和 PowerMock (Maven pom)
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
<!-- https://www.baeldung.com/intro-to-powermock -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
但是有很多关于如何设置 Mockito 和 PowerMock 的文档。
这是一个带有一些注释的可运行示例,让您大致了解如何使用模拟进行测试。
package __scratchpad__;
import static org.junit.Assert.fail;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(PingerTest.Pinger.class)
public class PingerTest {
// Inner class just to have a compact 'all in one' example
public class Pinger {
// refactored to better coding style (avoid 'Magic numbers')
// AND for testability because TIM-OUT is now accessible for test
public static final int TIME_OUT = 3000;
public boolean pingHost(String hostname, int port) {
try (Socket socket = new Socket()){
socket.connect(new InetSocketAddress(hostname, port), TIME_OUT);
return true;
} catch (IOException e) {
return false;
}
}
}
@Test
public void testPingHost() {
// create required mock objects to inject
InetSocketAddress addressMock = Mockito.mock(InetSocketAddress.class);
Socket socketMock = Mockito.mock(Socket.class);
// create parameters
final String hostname = "answer.deepthought";
final int port = 42;
final int timeOut = PingerTest.Pinger.TIME_OUT;
// create class under test
Pinger pingerUnderTest = new Pinger();
try {
// mock constructor call of class InetSocketAddress and verify correct parameters
// If parameters of call are different to the expected, than NO mock object is returned, the test will fail
// Otherwise the addressMock object is returned
whenNew(InetSocketAddress.class)
.withArguments(hostname, port)
.thenReturn(addressMock);
// mock constructor call of Socket class
whenNew(Socket.class)
.withNoArguments()
.thenReturn(socketMock);
// call method under test with parameters defined earlier.
// If parameters are different to them of InetSocketAddress constructor call the test will fail.
pingerUnderTest.pingHost(hostname, port);
// if all works fine exactly one call to method connect() with specified parameters MUST appear
// if there are more or less calls or any of the parameters is different, than the test will fail
Mockito.verify(socketMock).connect(addressMock, timeOut);
} catch(Exception e) {
e.printStackTrace();
fail("Unexpected exception caught!");
}
}
}