有很多方法来初始化使用一个的Mockito模拟对象。 什么是其中最好的方法是什么?
1。
public class SampleBaseTestCase {
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
2。
@RunWith(MockitoJUnitRunner.class)
[编辑] 3。
mock(XXX.class);
建议我,如果有任何其他的方式比这更好的?
对于嘲笑初始化 ,使用转轮或MockitoAnnotations.initMocks
是完全等同的解决方案。 从的javadoc的MockitoJUnitRunner :
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
)是比较经典的,我最喜欢。 该代码更简单。 使用转轮提供了很大的优势框架使用的自动验证 (通过描述@大卫华莱士在这个答案 )。
这两种解决方案允许共享的测试方法之间的嘲笑(和间谍)。 再加上@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 ...)
缺点:有更多的样板代码。 (该嘲笑创建)
我 recommandation是一种妥协。 使用@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
被实例化)。 没有样板代码。
缺点:测试不是自包含的代码,痛苦少
现在有(如v1.10.7)的第四个方式来实例化嘲笑,这是使用称为JUnit4 规则 MockitoRule 。
@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的子类 ,并用它们来包装测试语句的转轮提供 。 这样的结果是,你可以提取的@Before方法,@After方法,甚至尝试...捕捉到包装规则。 你可以用这些从测试中甚至相互作用,该方式的ExpectedException一样。
MockitoRule行为几乎完全一样MockitoJUnitRunner,但你可以使用任何其他选手,如参数 (它允许你的测试构造带参数,以便您的测试可以运行多次),或Robolectric的测试运行(所以它的类加载器可以提供Java的替代品对于Android原生班)。 这使得严格更加灵活最近JUnit和版本的Mockito使用。
综上所述:
-
Mockito.mock()
没有注解支持或使用验证直接调用。 -
MockitoAnnotations.initMocks(this)
:注释的支持,没有使用验证。 -
MockitoJUnitRunner
:注解支持和使用验证,但必须使用亚军。 -
MockitoRule
:注解支持和使用的验证与任何JUnit运行。
另请参见: 如何JUnit的@rule工作的?
MockitoAnnotations和亚军都得到了很好以上讨论,所以我要在我的两便士的没人爱抛出:
XXX mockedXxx = mock(XXX.class);
我用这个,因为我觉得它有点更具描述性的,我更喜欢(不出来正确的禁令)单元测试不使用成员变量,因为我喜欢我的测试是(尽可能多的,因为他们可以)自包含的。
其他答案是巨大的,包含更详细的,如果你想/需要他们。
除了这些,我想补充一个TL; DR:
- 喜欢使用
-
@RunWith(MockitoJUnitRunner.class)
- 如果你不能(因为你已经使用不同的亚军),喜欢使用
-
@Rule public MockitoRule rule = MockitoJUnit.rule();
- 类似(2),但你应该不再使用这样的:
-
@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
- 如果你想使用的测试只是一个模拟效果和不想暴露在其他测试在同一个测试类,使用
(1)和(2)和(3)是相互排斥的。
(4)可以组合使用与他人。