初始化模拟对象 – 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(尽可能多)自包含。