有许多方法可以使用mockit初始化模拟对象。哪一种方法是最好的?
1。
public class SampleBaseTestCase {
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
@RunWith(MockitoJUnitRunner.class)
mock(XXX.class);
建议我是否有比这些更好的方法…
对于模拟初始化,使用runner或MockitoAnnotations.initMocks
是严格等价的解决方案。从MockitoJUnitRunner的javadoc:
JUnit 4.5 runner初始化带有Mock注释的Mock,因此没有必要显式使用MockitoAnnotations.initMocks(Object)。模拟在每个测试方法之前初始化。
当您已经在测试用例上配置了一个特定的运行器(例如SpringJUnit4ClassRunner
)时,可以使用第一个解决方案(使用MockitoAnnotations.initMocks
)。
第二种解决方案(使用MockitoJUnitRunner
)更经典,也是我最喜欢的。代码更简单。使用运行器提供了自动验证框架使用(由@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));
}
}
优点:代码最少
缺点:黑魔法。在我看来,这主要是由于@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
是如何实例化的)。没有样板代码。
缺点:测试不是自包含的,更少的代码痛苦
现在(从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的子类,并使用它们包装Runner提供的测试语句。这样做的结果是,您可以提取@Before方法,@After方法,甚至尝试…将包装器捕获到规则中。您甚至可以在测试中与它们交互,就像ExpectedException那样。
MockitoRule的行为几乎与MockitoJUnitRunner完全相同,除了您可以使用任何其他运行程序,例如Parameterized(允许您的测试构造函数接受参数,以便您的测试可以多次运行)或Robolectric的测试运行程序(因此其类加载器可以为Android本机类提供Java替代)。这使得它在最近的JUnit和Mockito版本中使用起来更加灵活。
在简介:-
Mockito.mock()
:直接调用,不支持注释或使用验证。 -
MockitoAnnotations.initMocks(this)
:注释支持,没有使用验证。 -
MockitoJUnitRunner
:注释支持和使用验证,但你必须使用该运行器 -
MockitoRule
:任何JUnit运行器的注释支持和使用验证。
参见:JUnit @Rule是如何工作的?
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...
}
}
一个JUnit 5 Jupiter的小例子,"RunWith"被删除了,你现在需要使用扩展使用"@ExtendWith"注释。
@ExtendWith(MockitoExtension.class)
class FooTest {
@InjectMocks
ClassUnderTest test = new ClassUnderTest();
@Spy
SomeInject bla = new SomeInject();
}
MockitoAnnotations &上面已经讨论过了,所以我要把我的两便士给那些不被爱的人:
XXX mockedXxx = mock(XXX.class);
我使用这个是因为我发现它更具描述性,而且我更喜欢(不是完全禁止)单元测试不使用成员变量,因为我喜欢我的测试(尽可能多地)是自包含的。
有一种简洁的方法。
-
如果是单元测试,你可以这样做:
@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初始化mock):
@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() { } }
在Mockito
的最新版本中,方法MockitoAnnotations.initMocks
已被弃用
首选方法是使用
JUnit4
MockitoJUnitRunner
或MockitoRule
MockitoExtension
for JUnit5
MockitoTestNGListener
for TestNG
如果你不能使用专用的跑步者/扩展,你可以使用MockitoSession
其他答案是伟大的,包含更多的细节,如果你想/需要他们。
除此之外,我还想添加一个TL;DR:
- 优先使用
-
@RunWith(MockitoJUnitRunner.class)
-
- 如果不能(因为已经使用了不同的运行器),建议使用
-
@Rule public MockitoRule rule = MockitoJUnit.rule();
-
- 类似于(2),但你应该不再使用这个:
-
@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
-
- 如果您只希望在一个测试中使用mock,并且不希望将其暴露给同一测试类中的其他测试,则使用
-
X x = mock(X.class)
-
(1)、(2)和(3)是互斥的。
(4)可与其它组合使用