Unit testing Mockito:存根函数不工作
我正在使用编写一个简单的单元测试 然后,测试一个函数:Unit testing Mockito:存根函数不工作,unit-testing,junit,mockito,junit4,Unit Testing,Junit,Mockito,Junit4,我正在使用编写一个简单的单元测试 然后,测试一个函数: public class MyService { public void getData() { executor.execute(new MyRunnable() { @Override doTask() { MyRestClient client = getRestClient(); Response resp = client.getFromServ
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));
...
}
@测试
public void testGetData(){
MyService spyService=spy(MyService.getInstance());
//模拟客户端
MyRestClient mockedClient=mock(MyRestClient.class);
mockedClient.setData(“testData”);
//用于返回模拟客户端的stub getRestClient()函数
when(spyService.getRestClient())。然后返回(mockedClient);
//苏特
spyService.getData();
//运行可运行任务。
Mockito.doAnswer(新答案(){
公共对象应答(InvocationMock调用)引发异常{
对象[]args=invocation.getArguments();
Runnable Runnable=(Runnable)args[0];
runnable.doTask();
返回null;
}
}).when(executor).execute(Mockito.any(Runnable.class));
...
}
如上所述,我对getRestClient()
函数进行存根,以返回一个模拟的MyRestClient
。但是,当运行测试用例时,它不会存根getRestClient()
,而是运行真正的函数。为什么?[编辑]以下评论和评论反馈
经验法则是不要模仿被测试的类。此外,如果您的被测类不使用new
关键字,那么您的测试将更加容易。而是使用Factory
类来创建对象。无需使用Mockito.spy()
仅Mockito.mock()
下面的答案需要重要的测试设置,这一事实告诉您,MyService
有太多的责任,需要简化。然而,为了直接回答您的问题,这里是如何重构代码以支持使用mock验证对persist()
的调用
MyService
在构造函数中接受您将在测试设置中模拟的对象。将它们传递到构造函数中允许您的JUnit测试用例创建模拟,并保留对它们的引用以供稍后验证
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
被创建,以便在需要时可以对其进行隔离测试。我们再次将模拟对象注入构造函数。正如您在问题中所写的那样,很容易内联可运行项,但是您失去了控制在测试中创建的新实例的能力
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
类的实际实现紧密耦合。理想情况下,您需要的测试不需要了解被测类的内部工作情况。旁白:不使用doAnswer
,您可以使用获取Runnable
。为什么您试图在mock对象mockedClient.setData(“testData”)上设置一个值代码>?这一行实际上什么都不做。您也没有模拟getFromServer()
方法,因此您的示例代码可能缺少某些内容。我无法重现该问题。对我来说,在模拟的RestClient
上调用getFromServer()。你怎么知道getRestClient()
没有存根,我在你的代码中没有看到。