初始化模拟对象 – MockIto

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

1。

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

2。

 @RunWith(MockitoJUnitRunner.class) 

[编辑] 3。

 mock(XXX.class); 

build议我是否还有其他方法比这些更好…

对于 MockitoAnnotations.initMocks 初始化 ,使用runner或MockitoAnnotations.initMocks是严格等同的解决scheme。 从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.


当你已经在你的testing用例上configuration了一个特定的runner(例如MockitoAnnotations.initMocks )时,可以使用第一个解决scheme(使用MockitoAnnotations.initMocks )。

第二个解决scheme(与MockitoJUnitRunner )是更经典,我最喜欢的。 代码更简单。 使用runner提供了自动validation框架使用的巨大优势(在这个答案中由@David Wallace描述)。

这两个解决scheme都允许在testing方法之间共享模拟(和间谍)。 再加上@InjectMocks ,他们可以很快地编写unit testing。 样板模拟代码减less,testing更容易阅读。 例如 :

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

优点:代码是最小的

缺点:黑魔法。 国际海事组织主要是由于@InjectMocks注释。 有了这个注释“你放开了代码的痛苦” (请参阅@Brice的评论)


第三个解决scheme是创build你的模拟联合testing方法。 它允许@mlk在答案中解释了“ 自包含testing ”。

 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 …)

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


我的build议是妥协。 使用@RunWith(MockitoJUnitRunner.class)注释与@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是如何实例化的)。 没有样板代码。

缺点:testing不是自包含,代码痛苦less

现在有(第1.10.7节)第四种实例化mock的方法,它使用了一个名为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查找使用@Rule注释的TestRule的子类 ,并使用它们来包装Runner提供的testing语句 。 这样做的结果是你可以提取@Before方法,@After方法,甚至尝试…将包装捕获到规则中。 您甚至可以在testing中与ExpectedException进行交互。

MockitoRule的行为几乎和MockitoJUnitRunner一样 ,除了可以使用任何其他的参数 (如允许testing构造函数接受参数以便testing可以多次运行)或者Robolectric的testing运行器(所以它的类加载器可以提供Javareplace对于Android本机类)。 这使得它在最近的JUnit和Mockito版本中使用起来更为灵活。

综上所述:

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

另请参见: JUnit @Rule的工作原理

有这样一个干净的方法。

  • 如果是unit testing,你可以这样做:

     @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() { } } 
  • 编辑:如果这是一个集成testing,你可以做到这一点(不打算用这种方式与spring,只是展示你可以初始化与不同的亚军模拟):

     @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和跑步者已经在上面进行了很好的讨论,所以我将会为我所不喜欢的人们抛弃:

 XXX mockedXxx = mock(XXX.class); 

我使用这个,因为我觉得它有点更具描述性,我更喜欢(不是正确的禁止)unit testing不使用成员variables,因为我喜欢我的testing(尽可能多)自包含。