@RunWith(PowerMockRunner.class) vs @RunWith(MockitoJUnitRunner.class)

2022-09-02 00:55:43

在通常的 mocking with 和 annotations 中,所测试的类应与 一起运行。@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
  }
}

有人可以指出它有什么区别,以及我想使用一个而不是另一个吗?


答案 1

乍一看,答案很简单:嗯,有几种模拟框架,并且有不同的方法来使用它们。

第一个示例告诉 JUnit 使用 Mockito 模拟框架提供的“单元测试运行程序”。第二个示例使用 PowerMock 框架中的单元测试运行程序。

为了使事情有意义,您还将有不同的导入语句,因为两个框架对于@Mock注释具有不同的实现。

(使用这些特定于框架的测试运行程序的要点是,它们负责使用特定于框架的特殊注释初始化所有字段)。

所以:这里的区别很简单:第一个示例是使用Mockito框架编写的,第二个示例使用PowerMock。

现在,使用哪一个?

答:莫基托。

为什么?不知何故,一个丑陋的事实是:PowerMock-one基本上是一个求助的呼声。它说“测试的类设计得很糟糕,请修复它”。含义:作为开发人员,您可以编写“易于测试”的代码,或“难以测试”的代码。许多人做第二件事:他们写的代码很难测试。然后,PowerMock(ito)提供了仍然测试该代码的方法。

PowerMock(ito) 使您能够模拟(从而控制)对静态方法和 .为了实现这一点,PowerMock(ito)操作被测代码的字节码。对于小型代码库来说,这完全没问题,但是当您面对数百万行生产代码和数千个单元测试时,情况就完全不同了。new()

我见过许多PowerMock测试无缘无故地失败,以发现几个小时后...其他地方的一些“静态”东西被改变了,并且以某种方式影响了不同的PowerMock静态/新驱动测试用例。

在某个时候,我们的团队做出了一个有意识的决定:当你编写新代码时,你只能用PowerMock进行测试......这是不可接受的。从那时起,我们只创建了Mockito测试用例,从那以后,我们没有一次看到类似的奇怪问题,这些问题困扰着我们PowerMock。

使用PowerMock的唯一可接受的原因是当您想要测试您不想修改的现有(可能是第三方)代码时。但是,当然,测试这样的代码有什么意义呢?当您无法修改该代码时,为什么测试会突然失败?


答案 2

PowerMock永远不应该是你的首选。如果你只是写了一个类,它只能用PowerMock测试,你做错了什么。一个类应该有依赖注入或具有依赖关系的构造函数,所以测试是方便的,当然:不要尝试使用静态方法,因为这些方法在常规框架中是不可模拟的(read:mockito)。

另一方面:如果你有一个大项目,并且因为以前的开发人员没有这样做而想要向它添加单元测试,那么PowerMock可能是唯一的解决方案,而无需完全重构所有内容。从这个角度来看,我更喜欢PowerMock,而不是完全没有测试

PowerMock很脏,因为它更改了字节码,并且使用JaCoCo(SonarQube覆盖运行器)的代码覆盖率不起作用,但是IntelliJ代码覆盖运行器确实与PowerMock一起使用。

当在一个类中,一种方法不能用Mockito测试时,我将测试分开:一个测试类与Mockto,一个测试类与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());
   }
}

推荐