java - 使用 mockito.mockStatic 失败的单元测试
问题描述
所以,我正在编写一个 junit 测试,但我似乎无法弄清楚它为什么会失败。我正在使用 Mockito.mockStatic 来模拟 InetAddres.class。一次运行所有单元测试失败。分别运行它们会成功。我知道静态块被初始化一次。我似乎无法弄清楚为什么每个单元测试都没有重新初始化类 Host 。任何帮助表示赞赏
Ĵ
这是我的代码:
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import java.net.InetAddress;
import java.net.UnknownHostException;
import static org.assertj.core.api.Assertions.assertThat;
class HostTest {
@Test
void testLocalhost() {
try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
InetAddress inetAddress = Mockito.mock(InetAddress.class);
Mockito.when(inetAddress.getHostName()).thenReturn("LOCALHOST");
inetAddressMockedStatic.when(InetAddress::getLocalHost).thenReturn(inetAddress);
assertThat(Host.getLOCALHOST()).isEqualTo("LOCALHOST");
Mockito.reset(inetAddress);
}
}
@Test
void testIP() {
try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
InetAddress inetAddress = Mockito.mock(InetAddress.class);
Mockito.when(inetAddress.getHostAddress()).thenReturn("127.0.0.1");
inetAddressMockedStatic.when(InetAddress::getLocalHost).thenReturn(inetAddress);
assertThat(Host.getIP()).isEqualTo("127.0.0.1");
}
}
@Test
void testUnkownHostExceptionIP() {
try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
inetAddressMockedStatic.when(InetAddress::getLocalHost).thenThrow(UnknownHostException.class);
assertThat(Host.getIP()).isEqualTo("Unkown ip");
}
}
@Test
void testUnkownHostExceptionLocalhost() {
try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
inetAddressMockedStatic.when(InetAddress::getLocalHost).thenThrow(UnknownHostException.class);
assertThat(Host.getLOCALHOST()).isEqualTo("Unkown hostname");
}
}
}
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Host {
private static String LOCALHOST;
private static String IP;
static {
try {
InetAddress localhost = InetAddress.getLocalHost();
LOCALHOST = localhost.getHostName();
IP = localhost.getHostAddress();
} catch (UnknownHostException e) {
LOCALHOST = "Unkown hostname";
IP = "Unkown ip";
}
}
public static String getLOCALHOST() {
return LOCALHOST;
}
public static String getIP() {
return IP;
}
}
解决方案
静态初始化程序仅在加载类时执行一次。Host
这意味着它只会为使用该类的第一个测试用例运行。
在您的示例中,一旦testLocalhost
运行,该类将在 line 中使用 Host.getLOCALHOST()
,此时其初始化程序已被执行。它永远不会在其余的单元测试中再次运行。
如果你切换这些测试用例的顺序,你会得到不同的结果。
从您的测试用例来看,您可以做一些事情来使代码符合您的期望。由于 IP 和主机名会在您的程序执行期间发生变化,因此它们不应该是在静态初始化程序块中设置的静态成员。
摆脱共享状态。撇开并发性和内存可见性不谈,
static
成员对类的所有实例都是可见的。省略static
关键字并将它们变成常规字段public class Host { private final String hostName; private final String ip; // Constructor, use this to build new instances public Host(String hostName, String ip) { this.hostName = hostName; this.ip = ip; } // No longer static, this is now an instance method public getHostName() { return this.hostName; } public getIp() { return this.ip; } }
构建类的实例,将参数传递给构造函数以自定义其行为。
// Host.getIp(); // If IP and host name can vary, don't make them static InetAddress localhost = InetAddress.getLocalHost(); // build a new instance of Host, provide the relevant data at construction time Host testedHost = new Host(localhost.getHostName(), localhost.getHostAddress()); // call the instance method, this doesn't affect your other tests assertThat(testedHost.getIp()).is(someIp); // at this point, the Host instance you created may be garbage-collected to free memory (you don't need to do that yourself)
现在每个测试用例都将独立于其他测试用例。
Host
只需在每次需要时创建一个新实例。摆脱静态模拟。注意
InetAddress
方法调用是如何移到Host
类之外的。通过将它们传递给构造函数,您可以使代码更易于测试。实现了控制反转。
您可以使用工厂方法代替公共构造函数。底线是,如果您想让类改变其行为,通常最好创建新实例并封装任何状态。
静态类和成员更适合诸如在程序执行过程中不会改变的不可变内容,或不依赖任何内部状态的实用方法,即纯函数。
推荐阅读
- c# - OnCollisionEnter 在应该被调用的时候没有被调用
- sql - 如何将水晶报表代码中的If语句转换为SQL case语句
- c - 自定义 WM_APP 消息未发布在消息队列中
- python - 如何解决jupyter notebook中“启动内核失败”的问题
- amazon-web-services - AWS ECS 最近创建的实例没有配置 ssh authorized_keys
- python-3.x - 如何在python中同步两个音频文件?
- mysql - MySQL ")" 在这个位置无效,需要一个标识符
- java - 多对多关系上的 PostgreSQL 约束冲突错误,但应存在外键
- go - 符文与字节范围在字符串上
- c - 用运算符判断真假