@RunWith(PowerMockRunner.class) 与@RunWith(MockitoJUnitRunner.class)
@RunWith(PowerMockRunner.class) vs @RunWith(MockitoJUnitRunner.class)
在通常带有@Mock
和@InjectMocks
注释的模拟中,被测class应该是运行和@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 模拟框架提供的 "unit test runner"。第二个示例使用 PowerMock 框架中的单元测试运行器。
为了使事情有意义,您还需要不同的 import 语句,因为两个框架都有 不同的 实现,例如 @Mock 注释。
(使用这些特定于框架的测试运行器的要点是它们负责使用特定于框架的特殊注释初始化所有字段)。
所以:这里的区别很简单:第一个例子是用Mockito框架写的,第二个用的是PowerMock。
现在,使用哪一个?
答案:Mockito。
为什么?不知何故,一个丑陋的事实是:PowerMock-one 基本上是在求救。它说 "the class under test is badly designed, please fix it"。含义:作为开发人员,您可以编写 "easy to test" 代码,或 "hard to test" 代码。许多人做第二种:他们编写难以测试的代码。然后,PowerMock(ito) 提供了仍然 测试该代码的方法。
PowerMock(ito) 使您能够模拟(从而控制)对 static 方法和 new()
的调用。为了实现这一点,PowerMock(ito) 操纵您的 待测代码 的字节码。这对于小型代码库来说完全没问题,但是当您面对数百万行生产代码和数千个单元测试时,情况就完全不同了。
我看到许多 PowerMock 测试无缘无故失败,数小时后才发现……其他地方的一些 "static" 东西被改变了,并且不知何故影响了不同的 PowerMock static/new驱动测试用例。
在某些时候,我们的团队做出了一个有意识的决定:当您编写新代码时,您只能使用 PowerMock 对其进行测试……这是不可接受的。从那时起,我们只创建了 Mockito 测试用例,从那以后我们再也没有看到类似的怪异问题困扰我们 PowerMock。
使用 PowerMock 的唯一可接受的原因是当您想要测试您不想修改的现有(可能是第 3 方)代码时。但是当然,测试这样的代码有什么意义呢?当您无法修改该代码时,为什么测试会突然失败?
PowerMock 允许您模拟静态和私有方法以及最终的 类 等等。
PowerMock is a framework that extends other mock libraries such as
EasyMock with more powerful capabilities. PowerMock uses a custom
classloader and bytecode manipulation to enable mocking of static
methods, constructors, final classes and methods, private methods,
removal of static initializers and more.
如果您需要模拟这些类型的组件,可能会有一些代码味道,但是它可能会有用。在某些时候,您可能在一个过时的项目中工作,该项目创建了 静态助手 类,其中 需要模拟的依赖项 。如果您有能力更改架构,那么修复您的设计!否则,使用正确的技术进行测试。
如果您不需要模拟静态或私有函数,那么您就不需要使用 PowerMock。 PowerMock 是其他模拟框架的包装器。
PowerMock 绝不应该是您的首选。如果你只是写了一个只能用 PowerMock 测试的 class,你做错了什么。 class 应该具有依赖注入或具有依赖性的构造函数,以便于测试,当然:不要尝试使用静态方法,因为它们在常规框架中不可模拟(阅读:mockito)。
从另一方面来说:如果您有一个大项目,并且由于以前的开发人员没有这样做而您想向其中添加单元测试,那么 PowerMock 可能是唯一无需完全重构所有内容的解决方案。从这个角度来看,我更喜欢 PowerMock,而不是完全没有测试。
PowerMock 很脏,因为它更改了字节码,并且 JaCoCo(SonarQube 覆盖率运行器)的代码覆盖率不起作用,但 IntelliJ 代码覆盖率运行器可以与 PowerMock 一起使用。
当在一个 class 中无法使用 Mockito 测试一种方法时,我将测试分开:一项测试 class 使用 Mockito 和一项测试 class 使用 PowerMock。这将使您在 SonarQube 中的代码覆盖率更好。
public class ClassToTest {
public void testableMethod() {
/* Do something */
}
public String methodWithStaticCall() {
return MyTest.staticMethod();
}
}
那我有一个class来测试第一种方法:
@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());
}
}
在通常带有@Mock
和@InjectMocks
注释的模拟中,被测class应该是运行和@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 模拟框架提供的 "unit test runner"。第二个示例使用 PowerMock 框架中的单元测试运行器。
为了使事情有意义,您还需要不同的 import 语句,因为两个框架都有 不同的 实现,例如 @Mock 注释。
(使用这些特定于框架的测试运行器的要点是它们负责使用特定于框架的特殊注释初始化所有字段)。
所以:这里的区别很简单:第一个例子是用Mockito框架写的,第二个用的是PowerMock。
现在,使用哪一个?
答案:Mockito。
为什么?不知何故,一个丑陋的事实是:PowerMock-one 基本上是在求救。它说 "the class under test is badly designed, please fix it"。含义:作为开发人员,您可以编写 "easy to test" 代码,或 "hard to test" 代码。许多人做第二种:他们编写难以测试的代码。然后,PowerMock(ito) 提供了仍然 测试该代码的方法。
PowerMock(ito) 使您能够模拟(从而控制)对 static 方法和 new()
的调用。为了实现这一点,PowerMock(ito) 操纵您的 待测代码 的字节码。这对于小型代码库来说完全没问题,但是当您面对数百万行生产代码和数千个单元测试时,情况就完全不同了。
我看到许多 PowerMock 测试无缘无故失败,数小时后才发现……其他地方的一些 "static" 东西被改变了,并且不知何故影响了不同的 PowerMock static/new驱动测试用例。
在某些时候,我们的团队做出了一个有意识的决定:当您编写新代码时,您只能使用 PowerMock 对其进行测试……这是不可接受的。从那时起,我们只创建了 Mockito 测试用例,从那以后我们再也没有看到类似的怪异问题困扰我们 PowerMock。
使用 PowerMock 的唯一可接受的原因是当您想要测试您不想修改的现有(可能是第 3 方)代码时。但是当然,测试这样的代码有什么意义呢?当您无法修改该代码时,为什么测试会突然失败?
PowerMock 允许您模拟静态和私有方法以及最终的 类 等等。
PowerMock is a framework that extends other mock libraries such as EasyMock with more powerful capabilities. PowerMock uses a custom classloader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more.
如果您需要模拟这些类型的组件,可能会有一些代码味道,但是它可能会有用。在某些时候,您可能在一个过时的项目中工作,该项目创建了 静态助手 类,其中 需要模拟的依赖项 。如果您有能力更改架构,那么修复您的设计!否则,使用正确的技术进行测试。
如果您不需要模拟静态或私有函数,那么您就不需要使用 PowerMock。 PowerMock 是其他模拟框架的包装器。
PowerMock 绝不应该是您的首选。如果你只是写了一个只能用 PowerMock 测试的 class,你做错了什么。 class 应该具有依赖注入或具有依赖性的构造函数,以便于测试,当然:不要尝试使用静态方法,因为它们在常规框架中不可模拟(阅读:mockito)。
从另一方面来说:如果您有一个大项目,并且由于以前的开发人员没有这样做而您想向其中添加单元测试,那么 PowerMock 可能是唯一无需完全重构所有内容的解决方案。从这个角度来看,我更喜欢 PowerMock,而不是完全没有测试。
PowerMock 很脏,因为它更改了字节码,并且 JaCoCo(SonarQube 覆盖率运行器)的代码覆盖率不起作用,但 IntelliJ 代码覆盖率运行器可以与 PowerMock 一起使用。
当在一个 class 中无法使用 Mockito 测试一种方法时,我将测试分开:一项测试 class 使用 Mockito 和一项测试 class 使用 PowerMock。这将使您在 SonarQube 中的代码覆盖率更好。
public class ClassToTest {
public void testableMethod() {
/* Do something */
}
public String methodWithStaticCall() {
return MyTest.staticMethod();
}
}
那我有一个class来测试第一种方法:
@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());
}
}