在通常的带有@Mock
和@InjectMocks
注释的模拟中,被测试的类应该使用@RunWith(MockitoJUnitRunner.class)
运行。
@RunWith(MockitoJUnitRunner.class)
public class ReportServiceImplTestMockito {
@Mock
private TaskService mockTaskService;
@InjectMocks
private ReportServiceImpl service;
// Some tests
}
但在某些示例中,我看到正在使用@RunWith(PowerMockRunner.class)
:
@RunWith(PowerMockRunner.class)
public class Tests {
@Mock
private ISomething mockedSomething;
@Test
public void test1() {
// Is the value of mockedSomething here
}
@Test
public void test2() {
// Is a new value of mockedSomething here
}
}
有人可以指出有什么区别以及何时我想使用一个而不是另一个吗?
乍一看,答案很简单:嗯,有几个模拟框架,并且有不同的使用方法。
第一个示例告诉 JUnit 使用 Mockito 模拟框架提供的"单元测试运行器"。第二个示例使用 PowerMock 框架中的单元测试运行程序。
为了使事情有意义,您还将具有不同的import语句,例如,两个框架对@Mock注释都有不同的实现。
(使用这些特定于框架的测试运行程序的要点是它们负责使用特定于框架的特殊注释初始化所有字段)。
所以:这里的区别很简单:第一个示例是使用 Mockito 框架编写的,第二个示例使用 PowerMock。
现在,使用其中哪一个?
答:莫比托。
为什么?不知何故,一个丑陋的事实是:PowerMock-one基本上是一种求救声。它说"被测试的类设计得很糟糕,请修复它"。含义:作为开发人员,您可以编写"易于测试"的代码,或"难以测试"的代码。许多人做第二种:他们编写的代码很难测试。然后,PowerMock(ito)提供了仍然测试该代码的方法。
PowerMock(ito) 使您能够模拟(从而控制)对静态方法的调用,并new()
。为了实现这一点,PowerMock(ito)操作被测代码的字节码。这对于小型代码库来说是完全可以的,但是当你面对数百万行的生产代码和数千个单元测试时,情况就完全不同了。
我见过许多 PowerMock 测试无缘无故地失败,几个小时后才发现......其他地方的一些"静态"东西被更改了,并且以某种方式影响了不同的 PowerMock 静态/新驱动测试用例。
在某些时候,我们的团队做出了一个有意识的决定:当你编写新代码时,你只能用 PowerMock 进行测试......这是不可接受的。从那以后,我们只创建了 Mockito 测试用例,从那以后,我们一次也没有看到类似的奇怪问题困扰着我们 PowerMock。
使用 PowerMock 的唯一可接受的原因是,当您想要测试不想修改的现有(可能是第三方)代码时。但是,当然,测试这样的代码有什么意义呢?当您无法修改该代码时,为什么测试会突然失败?
PowerMock永远不应该是你的首选。如果你刚刚编写了一个只能用PowerMock测试的类,那么你做错了什么。一个类应该有一个依赖注入或一个带有依赖关系的构造函数,所以测试是方便的,当然:不要尝试使用静态方法,因为这些方法在常规框架中是不可模拟的(read:mockito)。
另一方面:如果你有一个大项目,并且因为以前的开发人员没有这样做而想要向其添加单元测试,PowerMock 可能是唯一的解决方案,而无需完全重构所有内容。从这个角度来看,我更喜欢PowerMock,而不是完全没有测试。
PowerMock很脏,因为它会改变字节码,并且使用JaCoCo(SonarQube覆盖运行器)的代码覆盖不起作用,但IntelliJ代码覆盖率运行器确实适用于PowerMock。
当在一个类中,一个方法无法使用 Mockito 进行测试时,我拆分了测试:一个测试类使用 Mockito,一个测试类使用 PowerMock。这将使您在SonarQube中的代码覆盖率更好。
public class ClassToTest {
public void testableMethod() {
/* Do something */
}
public String methodWithStaticCall() {
return MyTest.staticMethod();
}
}
然后我有一个类来测试第一种方法:
@RunWith(MockitoJUnitRunner.class)
public class testClassToTest() {
private sut = new ClassToTest();
@Test
public testMethod() {
sut.testableMethod();
}
}
还有一个带有PowerMock:
@RunWith(PowerMockJUnitRunner.class)
@PrepareForTest({MyTest.class, ClassToTest.class})
public class testClassToTestPM() {
private sut = new ClassToTest();
@Before
public void before() {
mockStatic(MyTest.class);
}
@Test
public testMethod() {
mockStatic(MyTest.class);
when(MyTest.staticMethod()).thenReturn("test");
assertEquals("test", sut.methodWithStaticCall());
}
}
PowerMock 允许您模拟静态和私有方法以及最终类等。
PowerMock是一个扩展其他模拟库的框架,例如 EasyMock具有更强大的功能。PowerMock 使用自定义 类加载器和字节码操作,以启用静态模拟 方法、构造函数、最终类和方法、私有方法、 删除静态初始值设定项等。
如果您需要模拟这些类型的组件,可能会有一些代码异味,但它可能很有用。在某些时候,您可能正在处理一个过时的项目,该项目创建了静态帮助程序类,这些类具有需要模拟的依赖项。如果你有能力改变架构,那就修复你的设计吧!否则,请使用正确的技术进行测试。
如果您不需要模拟静态或私有函数,则无需使用 PowerMock。PowerMock是其他模拟框架的包装器。