【问题标题】:@RunWith(PowerMockRunner.class) vs @RunWith(MockitoJUnitRunner.class)@RunWith(PowerMockRunner.class) 与 @RunWith(MockitoJUnitRunner.class)
【发布时间】:2016-07-08 14:17:41
【问题描述】:

在使用@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
  }
}

有人能指出有什么区别吗?什么时候我想用一个代替另一个?

【问题讨论】:

    标签: java mocking mockito powermockito


    【解决方案1】:

    乍一看,答案很简单:嗯,有几个模拟框架,并且有不同的使用方式。

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

    为了让事情变得有意义,您还可以使用不同的导入语句,例如,两个框架对@Mock 注释都有不同 实现。

    (使用这些特定于框架的测试运行器的主要目的是它们会使用特定于框架的特殊注释来初始化所有字段。

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

    现在,使用哪一个?

    答案:Mockito。

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

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

    我已经看到许多 PowerMock 测试无缘无故地失败,几个小时后才发现……其他地方的一些“静态”事物被改变了,并且以某种方式影响了不同的 PowerMock 静态/新驱动测试用例。

    在某个时候,我们的团队做出了一个有意识的决定:当您编写新代码时,您只能使用 PowerMock 进行测试……这是不可接受的。从那以后,我们只创建了 Mockito 测试用例,从那以后我们就再也没有遇到过类似的奇怪问题,让我们对 PowerMock 感到厌烦。

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

    【讨论】:

    • 个人对抗 PowerMock 是怎么回事!?现在,我知道它一个有某些问题的模拟库,但它是创建的(不是我,顺便说一句-我创建了另一个),以解决实际编写时最终出现的实际问题-世界测试。只是给你一个完全合法的真实世界情况,这样一个工具很有用:模拟FacesContext.getCurrentInstance()静态方法,这样你就可以验证对addMessage(...)方法的预期调用。此外,这里提出的其他主张实际上是错误的或值得商榷的。
    • 我同意;开发人员不应求助于模拟库以避免修复可测试性问题。这与他们避免良好的 OO 或 API 设计以解决模拟库的限制的情况一样真实。对我来说,这两种情况的最佳答案是完全避免嘲笑,并进行集成测试;这是我自己做的。
    • 想要测试不符合可测试设计的类是有正当理由的,例如,如果你想测试一个使用 lwjgl 的类,你要么必须创建一个只调用的薄层lwjgl 类静态方法,或者您可以对该类使用 powermock。需要重新创建您使用的 API 并且必须维护该层,更不用说您在需要高性能的非常紧凑的循环上添加额外的调用。通过使用自动化(并且本身经过测试)模拟框架来测试该代码将远远优于根本不测试。
    • @Eugene 实际上,你是对的。这是一个非常古老的答案,从我不得不每隔一周修复一次此类 PowerMock 问题开始。我修改了我的答案,我认为它 A)现在解决了这个问题,B)给出了一个更平衡的观点。但在这里要明确一点:4 年没有编写过一次 PowerMock 测试……我 100% 相信这有助于我们创建更好的代码。
    • @GhostCat 我很欣赏你的回答,因为它让我重新思考我的测试和我试图测试的课程。我确实重构了我正在测试的类以消除单行方法,然后再研究代码我意识到我不需要使用 PowerMock 的 whenNew。
    【解决方案2】:

    PowerMock 绝不应该是您的首选。如果您刚刚编写了一个只能使用 PowerMock 测试的类,那么您做错了。一个类应该具有依赖注入或具有依赖关系的构造函数,以便于测试,当然:不要尝试使用静态方法,因为它们在常规框架中是不可模拟的(阅读: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());
       }
    }
    

    【讨论】:

      【解决方案3】:

      PowerMock 允许您模拟静态和私有方法以及最终类等。

      PowerMock 是一个扩展其他模拟库的框架,例如 EasyMock 具有更强大的功能。 PowerMock 使用自定义 类加载器和字节码操作以启用静态模拟 方法,构造函数,最终类和方法,私有方法, 移除静态初始化器等等。

      如果您需要模拟这些类型的组件,可能会有一些代码气味它可能很有用。在某些时候,您可能在一个过时的项目中工作,该项目创建了 静态帮助类,这些类具有需要模拟的依赖项。如果你有能力改变架构,那么修复你的设计!否则,使用正确的技术进行测试。

      如果您不需要模拟静态或私有函数,则不需要使用 PowerMock。 PowerMock 是其他模拟框架的包装器。

      【讨论】:

      • 确实可能存在某种代码异味,但在我们开始更改代码之前,我们可能希望/需要对其进行某种测试,以确保我们的更改不会破坏代码... ;)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多