初始化模拟对象- Mockito

afdcj2ne  于 2022-11-08  发布在  其他
关注(0)|答案(8)|浏览(244)

使用MockIto初始化模拟对象有很多种方法,其中哪一种是最好的?
1.

public class SampleBaseTestCase {

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

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

pu3pd22g

pu3pd22g1#

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

JUnit 4.5 runner初始化使用Mock注解的mock,这样就不需要显式使用MockitoAnnotations.initMocks(Object)。在每个测试方法之前初始化mock。
当您已经在测试用例上配置了一个特定的runner(例如SpringJUnit4ClassRunner)时,可以使用第一个解决方案(使用MockitoAnnotations.initMocks)。
第二个解决方案(使用MockitoJUnitRunner)更经典,也是我最喜欢的。代码更简单。使用runner提供了自动验证框架使用的巨大优势(在此答案中由@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是如何示例化的)。没有样板代码。
缺点:测试不是独立的,代码的痛苦少

yfwxisqw

yfwxisqw2#

现在(从v1.10.7开始)有了第四种示例化mock的方法,即使用名为MockitoRule的JUnit 4 rule

@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,并使用它们来 * Package Runner提供的测试语句 。这样做的结果是,您可以提取@Before方法、@After方法,甚至尝试......将 Package 器捕获到规则中。您甚至可以在测试中与它们交互,就像ExpectedException所做的那样。
MockitoRule的行为 * 与MockitoJUnitRunner
几乎完全相同,只是您可以使用任何其他runner,如Parameterized(它允许测试构造函数接受参数,以便测试可以运行多次),或者Robolectric的测试runner(因此其类加载器可以提供Android原生类的Java替代)。这使得它在最近的JUnit和Mockito版本中使用起来更加灵活。
总而言之:

  • Mockito.mock():直接调用,不支持注解或使用验证。
  • MockitoAnnotations.initMocks(this):注解支持,无用法验证。
  • MockitoJUnitRunner:注解支持和使用验证,但必须使用该runner。
  • MockitoRule:支持任何JUnit运行程序的注解和使用验证。

另请参阅:How JUnit @Rule works?

7qhs6swi

7qhs6swi3#

1.使用模拟注解。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)

自JUnit 5起,已删除@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...
    }

}
0ve6wy6x

0ve6wy6x4#

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

@ExtendWith(MockitoExtension.class)
class FooTest {

  @InjectMocks
  ClassUnderTest test = new ClassUnderTest();

  @Spy
  SomeInject bla = new SomeInject();
}
vuktfyat

vuktfyat5#

MockitoAnnotations & the runner在上面已经讨论得很好了,所以我打算把我的两便士扔给那些不被爱的人:

XXX mockedXxx = mock(XXX.class);

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

lfapxunr

lfapxunr6#

有一个简洁的方法可以做到这一点。

  • 如果这是一个单元测试,您可以这样做:
@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中这样使用。只是展示一下你可以用不同的Runner初始化模拟):
@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() {
    }
}
8yoxcaq7

8yoxcaq77#

Mockito的最新版本中,不建议使用MockitoAnnotations.initMocks方法
首选方式是使用

如果您无法使用专用滑道/扩展,则可以使用MockitoSession

3pmvbmvn

3pmvbmvn8#

其他的答案很棒,如果你想/需要的话,包含更多的细节。
除此之外,我想补充一句TL;灾难恢复:

  • 偏好使用
  • @RunWith(MockitoJUnitRunner.class)
  • 如果不能(因为您已经使用了不同的流道),请使用
  • @Rule public MockitoRule rule = MockitoJUnit.rule();
  • 类似于(2),但您不应该再****使用它:
  • @Before public void initMocks() { MockitoAnnotations.initMocks(this); }
  • 如果您只想在一个测试中使用mock,而不想将它公开给同一个测试类中的其他测试,请使用
  • X x = mock(X.class)

(1)和(3)是互斥的。
(4)可以与其它组合使用。

相关问题