初始化模拟对象 - MockIto

时间:2013-03-19 08:49:22

标签: java junit mockito

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

1

 public class SampleBaseTestCase {

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

2

@RunWith(MockitoJUnitRunner.class)

[编辑] 3。

mock(XXX.class);

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

8 个答案:

答案 0 :(得分:123)

对于模拟初始化,使用跑步者或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)更经典,也是我的最爱。代码更简单。使用跑步者可以提供 automatic validation of framework usage 的巨大优势(@David Wallace中由this answer描述)。

两种解决方案都允许在测试方法之间共享模拟(和间谍)。与@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如何实例化)。没有样板代码。

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

答案 1 :(得分: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() { /* ... */ }
}

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

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

总结:

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

另请参阅:How JUnit @Rule works?

答案 2 :(得分: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() {
        }
    }
    
  • 编辑:如果是集成测试,你可以这样做(不打算以这种方式使用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() {
        }
    }
    

答案 3 :(得分:8)

MockitoAnnotations&上面已经对跑步者进行了很好的讨论,所以我要为不受爱的人投入我的钱:

XXX mockedXxx = mock(XXX.class);

我使用它是因为我觉得它更具描述性,我更喜欢(不是禁止右边)单元测试不使用成员变量,因为我喜欢我的测试(尽可能多)自包含。 / p>

答案 4 :(得分:4)

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

@ExtendWith(MockitoExtension.class)
class FooTest {

  @InjectMocks
  ClassUnderTest test = new ClassUnderTest();

  @Spy
  SomeInject bla = new SomeInject();
}

答案 5 :(得分:4)

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

首选方式是使用

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

答案 6 :(得分:0)

其他答案很好,如果您需要/需要它们,请提供更多详细信息。
除了这些,我还要添加一个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)可以结合使用。

答案 7 :(得分:0)

1.使用 MockitoAnnotations.openMocks()

Mockito 2 中的 MockitoAnnotations.initMock() 方法已弃用,并在 Mockito 3 中替换为 MockitoAnnotations.openMocks()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();
    }

}

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...
    }

}