简体   繁体   English

初始化模拟对象 - MockIto

[英]Initialising mock objects - MockIto

There are many ways to initialize a mock object using MockIto.有很多方法可以使用 MockIto 初始化模拟对象。 What is best way among these ?其中最好的方法是什么?

1. 1.

 public class SampleBaseTestCase {

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }
@RunWith(MockitoJUnitRunner.class)
mock(XXX.class);

suggest me if there are any other ways better than these...建议我是否有比这些更好的其他方法...

For the mocks initialization , using the runner or the MockitoAnnotations.initMocks are strictly equivalent solutions.对于MockitoAnnotations.initMocks初始化,使用 runner 或MockitoAnnotations.initMocks是严格等效的解决方案。 From the javadoc of the MockitoJUnitRunner :来自MockitoJUnitRunner的 javadoc :

JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. JUnit 4.5 runner 初始化用 Mock 注释的模拟,因此不需要显式使用 MockitoAnnotations.initMocks(Object)。 Mocks are initialized before each test method.在每个测试方法之前初始化模拟。


The first solution (with the MockitoAnnotations.initMocks ) could be used when you have already configured a specific runner ( SpringJUnit4ClassRunner for example) on your test case.第一个解决方案(与MockitoAnnotations.initMocks当你已经配置了特定的亚军()可用于SpringJUnit4ClassRunner您的测试案例为例)。

The second solution (with the MockitoJUnitRunner ) is the more classic and my favorite.第二种解决方案(使用MockitoJUnitRunner )更经典,也是我最喜欢的。 The code is simpler.代码更简单。 Using a runner provides the great advantage of automatic validation of framework usage (described by @David Wallace in this answer ).使用 runner 提供了自动验证框架使用情况的巨大优势(由@David Wallace本答案中描述)。

Both solutions allows to share the mocks (and spies) between the test methods.这两种解决方案都允许在测试方法之间共享模拟(和间谍)。 Coupled with the @InjectMocks , they allow to write unit tests very quickly.结合@InjectMocks ,它们可以非常快速地编写单元测试。 The boilerplate mocking code is reduced, the tests are easier to read.样板模拟代码减少了,测试更容易阅读。 For example:例如:

@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));
    }
}

Pros: The code is minimal优点:代码最少

Cons: Black magic.缺点:黑魔法。 IMO it is mainly due to the @InjectMocks annotation. IMO 主要是由于 @InjectMocks 注释。 With this annotation "you loose the pain of code" (see the great comments of @Brice )使用此注释“您可以摆脱代码的痛苦” (请参阅@Brice的精彩评论)


The third solution is to create your mock on each test method.第三种解决方案是在每个测试方法上创建模拟。 It allow as explained by @mlk in its answer to have " self contained test ".正如@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));
    }
}

Pros: You clearly demonstrate how your api works (BDD...)优点:你清楚地展示了你的 api 是如何工作的(BDD ...)

Cons: there is more boilerplate code.缺点:有更多样板代码。 (The mocks creation) (模拟创作)


My recommandation is a compromise.我的建议是妥协。 Use the @Mock annotation with the @RunWith(MockitoJUnitRunner.class) , but do not use the @InjectMocks :@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));
    }
}

Pros: You clearly demonstrate how your api works (How my ArticleManager is instantiated).优点:你清楚地表明你的API是如何工作的(我如何ArticleManager被实例化)。 No boilerplate code.没有样板代码。

Cons: The test is not self contained, less pain of code缺点:测试不是自包含的,代码的痛苦更少

There is now (as of v1.10.7) a fourth way to instantiate mocks, which is using a JUnit4 rule called MockitoRule .现在(从 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 looks for subclasses of TestRule annotated with @Rule , and uses them to wrap the test Statements that the Runner provides . JUnit 查找使用@Rule 注释的 TestRule 的子类,并使用它们来包装 Runner 提供的测试语句 The upshot of this is that you can extract @Before methods, @After methods, and even try...catch wrappers into rules.这样做的结果是您可以提取@Before 方法、@After 方法,甚至 try...catch 包装器到规则中。 You can even interact with these from within your test, the way thatExpectedException does.您甚至可以在测试中与这些进行交互,就像ExpectedException那样。

MockitoRule behaves almost exactly like MockitoJUnitRunner , except that you can use any other runner, such as Parameterized (which allows your test constructors to take arguments so your tests can be run multiple times), or Robolectric's test runner (so its classloader can provide Java replacements for Android native classes). MockitoRule 的行为几乎与 MockitoJUnitRunner 完全一样,除了您可以使用任何其他运行程序,例如Parameterized (允许您的测试构造函数接受参数,以便您的测试可以多次运行)或 Robolectric 的测试运行程序(因此它的类加载器可以提供 Java 替换)用于 Android 原生类)。 This makes it strictly more flexible to use in recent JUnit and Mockito versions.这使得在最近的 JUnit 和 Mockito 版本中使用起来更加灵活。

In summary:总之:

  • Mockito.mock() : Direct invocation with no annotation support or usage validation. Mockito.mock() :没有注释支持或使用验证的直接调用。
  • MockitoAnnotations.initMocks(this) : Annotation support, no usage validation. MockitoAnnotations.initMocks(this) :注解支持,没有使用验证。
  • MockitoJUnitRunner : Annotation support and usage validation, but you must use that runner. MockitoJUnitRunner :注释支持和使用验证,但您必须使用该运行程序。
  • MockitoRule : Annotation support and usage validation with any JUnit runner. MockitoRule :注解支持和任何 JUnit 运行器的使用验证。

See also: How JUnit @Rule works?另请参阅: JUnit @Rule 如何工作?

There is a neat way of doing this.有一种巧妙的方法可以做到这一点。

  • If it's an Unit Test you can do this:如果是单元测试,你可以这样做:

     @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() { } }
  • EDIT: If it's an Integration test you can do this(not intended to be used that way with Spring. Just showcase that you can initialize mocks with diferent Runners):编辑:如果它是一个集成测试,你可以这样做(不打算以这种方式与 Spring 一起使用。只是展示你可以用不同的 Runners 初始化模拟):

     @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() { } }

MockitoAnnotations & the runner have been well discussed above, so I'm going to throw in my tuppence for the unloved: MockitoAnnotations 和 runner 已经在上面进行了很好的讨论,所以我要为不受欢迎的人投入我的 tuppence:

XXX mockedXxx = mock(XXX.class);

I use this because I find it a little bit more descriptive and I prefer (not out right ban) unit tests not to use member variables as I like my tests to be (as much as they can be) self contained.我使用它是因为我发现它更具描述性,并且我更喜欢(不是完全禁止)单元测试不使用成员变量,因为我喜欢我的测试(尽可能多地)自包含。

A little example for JUnit 5 Jupiter, the "RunWith" was removed you now need to use the Extensions using the "@ExtendWith" Annotation. JUnit 5 Jupiter 的一个小例子,删除了“RunWith”,您现在需要使用“@ExtendWith”注解来使用扩展。

@ExtendWith(MockitoExtension.class)
class FooTest {

  @InjectMocks
  ClassUnderTest test = new ClassUnderTest();

  @Spy
  SomeInject bla = new SomeInject();
}

In te lastest version of Mockito the method MockitoAnnotations.initMocks is deprecated在TE最新版本Mockito方法MockitoAnnotations.initMocks已过时

Preferred way is use首选方式是使用

If you can not use dedicated runner/extension you can use MockitoSession如果您不能使用专用的运行程序/扩展程序,您可以使用MockitoSession

The other answers are great and contain more detail if you want/need them.其他答案很棒,如果您需要/需要它们,可以包含更多详细信息。
In addition to those, I would like to add a TL;DR:除此之外,我想添加一个 TL;DR:

  1. Prefer to use更喜欢使用
    • @RunWith(MockitoJUnitRunner.class)
  2. If you cannot (because you already use a different runner), prefer to use如果你不能(因为你已经使用了不同的跑步者),更喜欢使用
    • @Rule public MockitoRule rule = MockitoJUnit.rule();
  3. Similar to (2), but you should not use this anymore:与 (2) 类似,但您不应使用它:
    • @Before public void initMocks() { MockitoAnnotations.initMocks(this); }
  4. If you want to use a mock in just one of the tests and don't want to expose it to other tests in the same test class, use如果您只想在其中一个测试中使用模拟并且不想将其公开给同一测试类中的其他测试,请使用
    • X x = mock(X.class)

(1) and (2) and (3) are mutually exclusive. (1)和(2)和(3)是互斥的。
(4) can be used in combination with the others. (4) 可与其他组合使用。

1. Using MockitoAnnotations.openMocks() : 1. 使用 MockitoAnnotations.openMocks()

The MockitoAnnotations.initMock() method in Mockito 2 is deprecated and replaced with MockitoAnnotations.openMocks() in Mockito 3. The MockitoAnnotations.openMocks() method returns an instance of AutoClosable which can be used to close the resource after the test. Mockito 2 中的MockitoAnnotations.initMock()方法已弃用,并替换为MockitoAnnotations.openMocks() 3 中的MockitoAnnotations.openMocks()MockitoAnnotations.openMocks()方法返回AutoClosable一个实例,可用于在测试后关闭资源。 Below is an example using MockitoAnnotations.openMocks() .下面是一个使用MockitoAnnotations.openMocks()的例子。

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;


class MyTestClass {

    AutoCloseable openMocks;

    @BeforeEach
    void setUp() {
        openMocks = MockitoAnnotations.openMocks(this);
        // my setup code...
    }

    @Test
    void myTest() {
        // my test code...
        
    }

    @AfterEach
    void tearDown() throws Exception {
        // my tear down code...
        openMocks.close();
    }

}

2. Using @ExtendWith(MockitoExtension.class) : 2. 使用 @ExtendWith(MockitoExtension.class)

As of JUnit5 @RunWith has been removed.从 JUnit5 @RunWith ,@ @RunWith已被删除。 Below is an example using @ExtendWith :下面是一个使用@ExtendWith的例子:

@ExtendWith(MockitoExtension.class)
class MyTestClass {

    @BeforeEach
    void setUp() {
        // my setup code...
    }

    @Test
    void myTest() {
        // my test code...

    }

    @AfterEach
    void tearDown() throws Exception {
        // my tear down code...
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM