使用MockIto初始化模拟对象的方法有很多种。 这些中最好的方法是什么?
1
public class SampleBaseTestCase {
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
2
@RunWith(MockitoJUnitRunner.class)
[编辑] 3。
mock(XXX.class);
建议我是否有比这些更好的其他方法...
答案 0 :(得分:123)
对于模拟初始化,使用跑步者或MockitoAnnotations.initMocks
是严格等效的解决方案。来自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.
如果您已在测试用例中配置了特定的运行器(例如MockitoAnnotations.initMocks
),则可以使用第一个解决方案(使用SpringJUnit4ClassRunner
)。
第二种解决方案(使用MockitoJUnitRunner
)更经典,也是我的最爱。代码更简单。使用跑步者可以提供 automatic validation of framework usage 的巨大优势(@David Wallace中由this answer描述)。
两种解决方案都允许在测试方法之间共享模拟(和间谍)。与@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
如何实例化)。没有样板代码。
缺点:测试不是自包含的,代码的痛苦
答案 1 :(得分:24)
现在(从v1.10.7开始)第四种实例化模拟的方法,即使用名为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查找subclasses of TestRule annotated with @Rule,并使用它们来包装Runner提供的测试语句。这样做的结果是你可以提取@Before方法,@ After方法,甚至尝试...将包装器捕获到规则中。您甚至可以在测试中与ExpectedException的方式进行交互。
MockitoRule的行为几乎与MockitoJUnitRunner 完全相同,只不过你可以使用任何其他运行器,例如Parameterized(它允许你的测试构造函数接受参数,这样你的测试就可以运行多次了),或Robolectric的测试运行器(因此其类加载器可以为Android本机类提供Java替换)。这使得它在最近的JUnit和Mockito版本中使用时更加灵活。
总结:
Mockito.mock()
:没有注释支持或使用验证的直接调用。MockitoAnnotations.initMocks(this)
:注释支持,无使用验证。MockitoJUnitRunner
:注释支持和使用验证,但您必须使用该运行器。MockitoRule
:使用任何JUnit运行程序进行注释支持和使用验证。答案 2 :(得分:9)
有一种巧妙的方法可以做到这一点。
如果是单元测试,你可以这样做:
@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。只是展示你可以用不同的Runners初始化模拟):
@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() {
}
}
答案 3 :(得分:8)
MockitoAnnotations&上面已经对跑步者进行了很好的讨论,所以我要为不受爱的人投入我的钱:
XXX mockedXxx = mock(XXX.class);
我使用它是因为我觉得它更具描述性,我更喜欢(不是禁止右边)单元测试不使用成员变量,因为我喜欢我的测试(尽可能多)自包含。 / p>
答案 4 :(得分:4)
JUnit 5 Jupiter的一个小示例,删除了“ RunWith”,现在您需要使用带有“ @ExtendWith”注释的扩展。
@ExtendWith(MockitoExtension.class)
class FooTest {
@InjectMocks
ClassUnderTest test = new ClassUnderTest();
@Spy
SomeInject bla = new SomeInject();
}
答案 5 :(得分:4)
在 Mockito
的最新版本中,MockitoAnnotations.initMocks
方法已被弃用
首选方式是使用
MockitoJUnitRunner
或 MockitoRule
代表 JUnit4
MockitoExtension
为 JUnit5
MockitoTestNGListener
为 TestNG
如果您不能使用专用的运行器/扩展程序,您可以使用 MockitoSession
答案 6 :(得分:0)
其他答案很好,如果您需要/需要它们,请提供更多详细信息。
除了这些,我还要添加一个TL; DR:
@RunWith(MockitoJUnitRunner.class)
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
X x = mock(X.class)
(1)和(2)和(3)是互斥的。
(4)可以结合使用。
答案 7 :(得分:0)
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...
}
}