我正在使用Mockito编写一个简单的单元测试。
然后,测试中的函数:
public class MyService {
public void getData() {
executor.execute(new MyRunnable() {
@Override
doTask() {
MyRestClient client = getRestClient();
Response resp = client.getFromServer();
persist(resp.getData());
}
});
}
}
protected MyRestClient getRestClient() {
return new MyRestClient();
}
我的测试用例,我想测试doTask()
已经运行& resp.getData()
被保留:
@Test
public void testGetData() {
MyService spyService = spy(MyService.getInstance());
// mock client
MyRestClient mockedClient = mock(MyRestClient.class);
mockedClient.setData("testData");
// stub getRestClient() function to return mocked client
when(spyService.getRestClient()).thenReturn(mockedClient);
// SUT
spyService.getData();
// run the Runnable task.
Mockito.doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Exception {
Object[] args = invocation.getArguments();
Runnable runnable = (Runnable) args[0];
runnable.doTask();
return null;
}
}).when(executor).execute(Mockito.any(Runnable.class));
...
}
如上所述,我将getRestClient()
函数存根以返回模拟 MyRestClient
。但是,在运行测试用例时,它不会对getRestClient()
进行存根,而是运行实际函数。为什么呢?
答案 0 :(得分:-1)
[编辑]以下评论和评论反馈
经验法则不是嘲笑被测试的班级。如果您所测试的课程不使用new
关键字,您的测试也会更容易。而是使用Factory
类来创建对象。不需要仅使用Mockito.spy()
Mockito.mock()
。
以下答案需要进行大量测试设置这一事实告诉您MyService
具有过多的重复性,需要简化。但是,为了直接回答您的问题,您可以重构代码以支持使用Mocks验证对persist()
的调用。
MyService
在构造函数中接受您将在测试设置中进行模拟的对象。将它们传递给构造函数允许您的JUnit测试用例创建Mocks并保留对它们的引用以便稍后进行验证。
public class MyService {
private MyRunnableFactory runFactory;
private MyRestClientFactory restFactory;
private MyRestDao dao;
// inject constructor arguments
public MyService(MyRunnableFactory runFactory, MyRestClientFactory restFactory, MyRestDao dao) {
this.runFactory = runFactory;
this.restFactory = restFactory;
this.dao = dao;
}
public void getData() {
MyRestClient restClient = restFactory.createInstance();
MyRunnable runner = runFactory.createInstance(restClient, dao);
executor.execute(runner);
}
}
创建 MyRunnable
,以便在需要时可以单独进行测试。我们再次将Mock对象注入构造函数中。正如您在问题中所写的那样内联Runnables很有诱惑力,但是您无法控制在测试中创建的new
实例。
public class MyRunnable implements Runnable {
private MyRestClient restClient;
private MyRestDao dao;
public MyRunnable(MyRestClient restClient, MyRestDao dao) {
this.restClient = restClient;
this.dao = dao;
}
public void run() {
Response resp = restClient.getFromServer();
dao.persist(resp.getData());
}
}
创建了 MyRestDao
,因为这是您要在测试用例中验证的类。我没有看到你的问题中定义了persist()的位置,因此我们创建了一个数据访问对象(DAO)来实现它。
public class MyRestDao {
public void persist() {
// save to some repository
}
}
现在让我们编写使用上述类的测试用例。我们想验证是否已调用persist()方法
@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
@Mock MyRestDao dao;
@Mock MyRestClient restClient;
@Mock MyRunnableFactory runFactory;
@Mock MyRestClientFactory restFactory;
@Test
public void testPersistIsCalled() {
Response expectedResponse = new Response("some data"); // real implementation, not mocked
MyRunnable runner = new MyRunnable(restClient, dao); // real implementation, not mocked
when(restFactory.createInstance()).thenReturn(restClient);
when(runFactory.createInstance(restClient, dao)).thenReturn(runner);
when(restClient.getFromServer()).thenReturn(expectedResponse);
when(restClient.getData()).thenReturn(myRunnable);
// method under test
MyService service = new MyService(runFactory, restFactory);
service.getData();
verify(dao).persist(expectedResponse.getData());
}
}
请注意,此测试用例很脆弱,因为它与MyService
类的实际实现紧密耦合。理想情况下,您希望测试不需要了解所测试类的内部工作原理。