初始化模拟对象 \- Mockito

新手上路,请多包涵

使用 MockIto 初始化模拟对象的方法有很多种。其中最好的方法是什么?

1.

  public class SampleBaseTestCase {

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }

 @RunWith(MockitoJUnitRunner.class)

 mock(XXX.class);

建议我是否有比这些更好的其他方法……

原文由 Vinay Veluri 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 540
2 个回答

对于模拟初始化,使用 runner 或 MockitoAnnotations.initMocks 是严格等效的解决方案。来自 MockitoJUnitRunner 的 javadoc:

JUnit 4.5 runner 初始化用 Mock 注释的模拟,因此不需要显式使用 MockitoAnnotations.initMocks(Object)。在每个测试方法之前初始化模拟。


当您已经在测试用例上配置了特定的运行器(例如 SpringJUnit4ClassRunner )时,可以使用第一个解决方案(使用 MockitoAnnotations.initMocks )。

第二种解决方案(带有 MockitoJUnitRunner )更经典,也是我最喜欢的。代码更简单。使用运行器提供了 框架使用自动验证 的巨大优势( @David Wallace这个答案 中描述)。

这两种解决方案都允许在测试方法之间共享模拟(和间谍)。加上 @InjectMocks ,它们允许非常快速地编写单元测试。样板模拟代码减少了,测试更容易阅读。例如:

 @RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock(name = "database") private ArticleDatabase dbMock;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @InjectMocks private ArticleManager manager;

    @Test public void shouldDoSomething() {
        manager.initiateArticle();
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        manager.finishArticle();
        verify(database).removeListener(any(ArticleListener.class));
    }
}

优点:代码最少

缺点:黑魔法。 IMO 这主要是由于 @InjectMocks 注释。有了这个注释 “你就摆脱了代码的痛苦” (见 @Brice 的精彩评论)


第三种解决方案是在每个测试方法上创建您的模拟。正如 @mlk 在其答案中所解释的那样,它允许进行“ _自包含测试_”。

 public class ArticleManagerTest {

    @Test public void shouldDoSomething() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when
        manager.initiateArticle();

        // then
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when
        manager.finishArticle();

        // then
        verify(database).removeListener(any(ArticleListener.class));
    }
}

优点:你清楚地展示了你的 api 是如何工作的(BDD …)

缺点:有更多样板代码。 (模拟创作)


的建议是妥协。将 @Mock 注释与 @RunWith(MockitoJUnitRunner.class) 一起使用,但不要使用 @InjectMocks

 @RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock private ArticleDatabase database;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @Test public void shouldDoSomething() {
        // given
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when
        manager.initiateArticle();

        // then
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleManager manager = new ArticleManager(calculator,
                                                    userProvider,
                                                    database);

        // when
        manager.finishArticle();

        // then
        verify(database).removeListener(any(ArticleListener.class));
    }
}

优点:你清楚地展示了你的 api 是如何工作的(我的 ArticleManager 是如何实例化的)。没有样板代码。

缺点:测试不是独立的,减少了代码的痛苦

原文由 gontard 发布,翻译遵循 CC BY-SA 4.0 许可协议

现在(从 v1.10.7 开始)有第四种方法来实例化模拟,它使用名为 MockitoRule 的 JUnit4 _规则_。

 @RunWith(JUnit4.class)   // or a different runner of your choice
public class YourTest
  @Rule public MockitoRule rule = MockitoJUnit.rule();
  @Mock public YourMock yourMock;

  @Test public void yourTestMethod() { /* ... */ }
}

JUnit 寻找 用@Rule 注解的 TestRule 的子类,并使用它们来 _包装 Runner 提供的测试语句_。这样做的结果是您可以将 @Before 方法、@After 方法甚至 try…catch 包装器提取到规则中。您甚至可以在测试中与这些交互,就像 ExpectedException 所做的那样。

MockitoRule 的行为 _几乎与 MockitoJUnitRunner 完全相同_,除了您可以使用任何其他运行器,例如 Parameterized (它允许您的测试构造函数接受参数,以便您的测试可以多次运行),或 Robolectric 的测试运行器(因此它的类加载器可以提供 Java 替代品适用于 Android 原生类)。这使得它在最近的 JUnit 和 Mockito 版本中使用起来更加灵活。

总之:

  • Mockito.mock() :直接调用,没有注释支持或使用验证。
  • MockitoAnnotations.initMocks(this) :注释支持,无使用验证。
  • MockitoJUnitRunner :注释支持和使用验证,但您必须使用该跑步者。
  • MockitoRule :任何 JUnit 运行程序的注释支持和使用验证。

另请参阅: JUnit @Rule 的工作原理?

原文由 Jeff Bowman 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题