使用 Guice,如何将单元测试中的模拟对象注入到正在测试的 class 中
Using Guice, how do I inject a mock object from my unit test, into the class being tested
考虑以下代码:
@Singleton
public class MyServiceImpl {
public int doSomething() {
return 5;
}
}
@ImplementedBy(MyServiceImpl.class)
public interface MyService {
public int doSomething();
}
public class MyCommand {
@Inject private MyService service;
public boolean executeSomething() {
return service.doSomething() > 0;
}
}
public class MyCommandTest {
@InjectMocks MyServiceImpl serviceMock;
private MyCommand command;
@Before public void beforeEach() {
MockitoAnnotations.initMocks(this);
command = new MyCommand();
when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
}
@Test public void mockInjected() {
boolean result = command.executeSomething();
verify(serviceMock).doSomething();
assertThat(result, equalTo(false));
}
}
当我尝试在我的模拟实现对象上存根 doSomething() 方法时,我的测试失败了。我收到错误:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified. Mocking methods
declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
我是通过 Guice 进行依赖注入的新手,不确定为什么我不能以这种方式模拟实现对象?
不使用 CDI 进行测试
一个简单的解决方案是将 CDI 与 Constructor Injection 相结合,而忽略 Guice 进行测试:
public class MyCommand {
private final MyService service;
@Inject
public MyCommand(MyService service) {
this.service = service;
}
public boolean executeSomething() {
return service.doSomething() > 0;
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyCommandTest {
@Mock
MyServiceImpl serviceMock;
private MyCommand command;
@Before public void beforeEach() {
MockitoAnnotations.initMocks(this);
when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
// inject without Guice
command = new MyCommand(serviceMock);
}
}
使用 Mockito 进行 CDI 测试
否则,如果你不喜欢构造函数注入,测试代码应该是这样的:
@RunWith(MockitoJUnitRunner.class)
public class MyCommandTest {
@Mock
MyServiceImpl serviceMock;
@InjectMocks
private MyCommand command;
private AutoCloseable mockHandler;
@Before
public void beforeEach() {
// initialize members annotated with @Mock and @InjectMocks
mockHandler = MockitoAnnotations.openMocks(this);
when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
}
@After
public void afterEach() throws Exception {
mockHandler.close();
}
}
考虑以下代码:
@Singleton
public class MyServiceImpl {
public int doSomething() {
return 5;
}
}
@ImplementedBy(MyServiceImpl.class)
public interface MyService {
public int doSomething();
}
public class MyCommand {
@Inject private MyService service;
public boolean executeSomething() {
return service.doSomething() > 0;
}
}
public class MyCommandTest {
@InjectMocks MyServiceImpl serviceMock;
private MyCommand command;
@Before public void beforeEach() {
MockitoAnnotations.initMocks(this);
command = new MyCommand();
when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
}
@Test public void mockInjected() {
boolean result = command.executeSomething();
verify(serviceMock).doSomething();
assertThat(result, equalTo(false));
}
}
当我尝试在我的模拟实现对象上存根 doSomething() 方法时,我的测试失败了。我收到错误:
org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'. For example: when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because: 1. you stub either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified. Mocking methods declared on non-public parent classes is not supported. 2. inside when() you don't call method on mock but on some other object.
我是通过 Guice 进行依赖注入的新手,不确定为什么我不能以这种方式模拟实现对象?
不使用 CDI 进行测试
一个简单的解决方案是将 CDI 与 Constructor Injection 相结合,而忽略 Guice 进行测试:
public class MyCommand {
private final MyService service;
@Inject
public MyCommand(MyService service) {
this.service = service;
}
public boolean executeSomething() {
return service.doSomething() > 0;
}
}
@RunWith(MockitoJUnitRunner.class)
public class MyCommandTest {
@Mock
MyServiceImpl serviceMock;
private MyCommand command;
@Before public void beforeEach() {
MockitoAnnotations.initMocks(this);
when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
// inject without Guice
command = new MyCommand(serviceMock);
}
}
使用 Mockito 进行 CDI 测试
否则,如果你不喜欢构造函数注入,测试代码应该是这样的:
@RunWith(MockitoJUnitRunner.class)
public class MyCommandTest {
@Mock
MyServiceImpl serviceMock;
@InjectMocks
private MyCommand command;
private AutoCloseable mockHandler;
@Before
public void beforeEach() {
// initialize members annotated with @Mock and @InjectMocks
mockHandler = MockitoAnnotations.openMocks(this);
when(serviceMock.doSomething()).thenReturn(-1); // <- Error here
}
@After
public void afterEach() throws Exception {
mockHandler.close();
}
}