初始化模拟对象 - 的Mockito(Initialising mock objects - Moc

2019-07-21 22:50发布

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

1。

 public class SampleBaseTestCase {

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

2。

@RunWith(MockitoJUnitRunner.class)

[编辑] 3。

mock(XXX.class);

建议我,如果有任何其他的方式比这更好的?

Answer 1:

对于嘲笑初始化 ,使用转轮或MockitoAnnotations.initMocks是完全等同的解决方案。 从的javadoc的MockitoJUnitRunner :

JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.


第一个解决方案(与MockitoAnnotations.initMocks当你已经配置了特定的亚军()可用于SpringJUnit4ClassRunner您的测试案例为例)。

第二个解决方案(与MockitoJUnitRunner )是比较经典的,我最喜欢。 该代码更简单。 使用转轮提供了很大的优势框架使用的自动验证 (通过描述@大卫华莱士在这个答案 )。

这两种解决方案允许共享的测试方法之间的嘲笑(和间谍)。 再加上@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 ...)

缺点:有更多的样板代码。 (该嘲笑创建)


recommandation是一种妥协。 使用@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被实例化)。 没有样板代码。

缺点:测试不是自包含的代码,痛苦少



Answer 2:

现在有(如v1.10.7)的第四个方式来实例化嘲笑,这是使用称为JUnit4 规则 MockitoRule 。

@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的子类 ,并用它们来包装测试语句的转轮提供 。 这样的结果是,你可以提取的@Before方法,@After方法,甚至尝试...捕捉到包装规则。 你可以用这些从测试中甚至相互作用,该方式的ExpectedException一样。

MockitoRule行为几乎完全一样MockitoJUnitRunner,但你可以使用任何其他选手,如参数 (它允许你的测试构造带参数,以便您的测试可以运行多次),或Robolectric的测试运行(所以它的类加载器可以提供Java的替代品对于Android原生班)。 这使得严格更加灵活最近JUnit和版本的Mockito使用。

综上所述:

  • Mockito.mock()没有注解支持或使用验证直接调用。
  • MockitoAnnotations.initMocks(this) :注释的支持,没有使用验证。
  • MockitoJUnitRunner :注解支持和使用验证,但必须使用亚军。
  • MockitoRule :注解支持和使用的验证与任何JUnit运行。

另请参见: 如何JUnit的@rule工作的?



Answer 3:

有这样的一种巧妙的方法。

  • 如果它是一个单元测试,你可以这样做:

     @RunWith(MockitoJUnitRunner.class) public class MyUnitTest { @Mock private MyFirstMock myFirstMock; @Mock private MySecondMock mySecondMock; @Spy private MySpiedClass mySpiedClass = new MySpiedClass(); // It's gonna inject the 2 mocks and the spied object per reflection to this object // The java doc of @InjectMocks explains it really well how and when it does the injection @InjectMocks private MyClassToTest myClassToTest; @Test public void testSomething() { } } 
  • 编辑:如果它是一个集成测试,你可以做到这一点(不打算使用Spring中的一些方式只是展示,你可以初始化diferent运动员嘲笑。):

     @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("aplicationContext.xml") public class MyIntegrationTest { @Mock private MyFirstMock myFirstMock; @Mock private MySecondMock mySecondMock; @Spy private MySpiedClass mySpiedClass = new MySpiedClass(); // It's gonna inject the 2 mocks and the spied object per reflection to this object // The java doc of @InjectMocks explains it really well how and when it does the injection @InjectMocks private MyClassToTest myClassToTest; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } @Test public void testSomething() { } } 


Answer 4:

MockitoAnnotations和亚军都得到了很好以上讨论,所以我要在我的两便士的没人爱抛出:

XXX mockedXxx = mock(XXX.class);

我用这个,因为我觉得它有点更具描述性的,我更喜欢(不出来正确的禁令)单元测试不使用成员变量,因为我喜欢我的测试是(尽可能多的,因为他们可以)自包含的。



Answer 5:

其他答案是巨大的,包含更详细的,如果你想/需要他们。
除了这些,我想补充一个TL; DR:

  1. 喜欢使用
    • @RunWith(MockitoJUnitRunner.class)
  2. 如果你不能(因为你已经使用不同的亚军),喜欢使用
    • @Rule public MockitoRule rule = MockitoJUnit.rule();
  3. 类似(2),但你应该不再使用这样的:
    • @Before public void initMocks() { MockitoAnnotations.initMocks(this); }
  4. 如果你想使用的测试只是一个模拟效果和不想暴露在其他测试在同一个测试类,使用
    • X x = mock(X.class)

(1)和(2)和(3)是相互排斥的。
(4)可以组合使用与他人。



文章来源: Initialising mock objects - MockIto