初始化模拟对象 - MockIto

Vin*_*uri 107 java junit mockito

有许多方法可以使用MockIto初始化模拟对象.这些中最好的方法是什么?

1.

 public class SampleBaseTestCase {

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }
Run Code Online (Sandbox Code Playgroud)

2.

@RunWith(MockitoJUnitRunner.class)
Run Code Online (Sandbox Code Playgroud)

[编辑] 3.

mock(XXX.class);
Run Code Online (Sandbox Code Playgroud)

建议我,如果有任何其他方法比这些更好......

gon*_*ard 132

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

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)是更经典和我最喜欢的.代码更简单.使用跑步者提供了框架使用自动验证的巨大优势(在本回答中由@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));
    }
}
Run Code Online (Sandbox Code Playgroud)

优点:代码很少

缺点:黑魔法.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));
    }
}
Run Code Online (Sandbox Code Playgroud)

优点:你清楚地展示了你的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));
    }
}
Run Code Online (Sandbox Code Playgroud)

优点:您清楚地演示了您的api如何工作(如何ArticleManager实例化).没有样板代码.

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

  • 这两者是等价的是不正确的.使用"MockitoJUnitRunner"的唯一优势是更简单的代码是不正确的.有关这些差异的更多信息,请参阅http://stackoverflow.com/questions/10806345/runwithmockitojunitrunner-class-vs-mockitoannotations-initmocksthis/10812752#10812752上的问题以及我对它的回答. (6认同)
  • @Gontard是的,确定依赖是可见的,但我看到使用这种方法出错了.关于使用`Collaborator collab = mock(Collaborator.class)`,在我看来这种方式肯定是一种有效的方法.虽然这可能会很冗长,但您可以获得测试的可理解性和可重构性.两种方式各有利弊,我还没有决定哪种方法更好.Amyway总是可以编写垃圾,可能取决于上下文和编码器. (2认同)

Tha*_*thu 34

1. 使用 MockitoAnnotations.openMocks()

Mockito 2 中的方法MockitoAnnotations.initMock()已弃用,并MockitoAnnotations.openMocks()在 Mockito 3 中替换。该MockitoAnnotations.openMocks()方法返回一个实例,AutoClosable可用于在测试后关闭资源。下面是一个使用的示例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();
    }

}
Run Code Online (Sandbox Code Playgroud)

2.使用@ExtendWith(MockitoExtension.class)

从 JUnit5 开始@RunWith已被删除。下面是一个使用的示例@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...
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 实际上它已在 [Mockito 3.4.0](https://www.javadoc.io/doc/org.mockito/mockito-core/3.4.0/org/mockito/MockitoAnnotations.html) 中被弃用,而不是在 Mockito 3 中.在[Mockito 3.3.0](https://www.javadoc.io/doc/org.mockito/mockito-core/3.3.0/org/mockito/MockitoAnnotations.html)中找不到`openMocks`方法 (2认同)

Jef*_*ica 24

现在(从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() { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

JUnit查找使用@Rule注释的TestRule的子类,并使用它们来包装Runner提供的测试语句.这样做的结果是你可以提取@Before方法,@ After方法,甚至尝试...将包装器捕获到规则中.您甚至可以在测试中与ExpectedException的方式进行交互.

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

综上所述:

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

另请参阅:JUnit @Rule如何工作?

  • 在Kotlin中,规则如下所示:`@get:Rule val mockitoRule:MockitoRule = MockitoJUnit.rule()` (3认同)

emd*_*emd 9

有一种巧妙的方法可以做到这一点.

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

    @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() {
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 编辑:如果它是一个集成测试,你可以这样做(不打算用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() {
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 实际上不会,您的权利。我只是想展示Mockito的可能性。例如,如果您使用RESTFuse,则必须使用其跑步者,以便可以使用MockitoAnnotations.initMocks(this);初始化模拟。 (2认同)

Mic*_*mlk 8

MockitoAnnotations和跑步者已经在上面讨论得很好了,所以我要把我的tuppence扔给不受爱的人:

XXX mockedXxx = mock(XXX.class);
Run Code Online (Sandbox Code Playgroud)

我使用它是因为我发现它更具描述性,而且我更喜欢(不是正确禁止)单元测试不使用成员变量,因为我喜欢我的测试(尽可能多)自包含.

  • 为了阅读测试,必须要了解更少的魔力.你声明变量,给它一个值 - 没有注释,反射等. (3认同)

fl0*_*l0w 8

JUnit 5 Jupiter 的一个小示例,删除了“RunWith”,您现在需要使用“@ExtendWith”注释来使用扩展。

@ExtendWith(MockitoExtension.class)
class FooTest {

  @InjectMocks
  ClassUnderTest test = new ClassUnderTest();

  @Spy
  SomeInject bla = new SomeInject();
}
Run Code Online (Sandbox Code Playgroud)


Sla*_*ski 7

Mockito该方法的最新版本MockitoAnnotations.initMocks已被弃用

首选方式是使用

如果您不能使用专用的运行程序/扩展程序,您可以使用MockitoSession