Mockito:存根功能不起作用

时间:2015-12-03 11:20:37

标签: unit-testing junit mockito junit4

我正在使用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()进行存根,而是运行实际函数。为什么呢?

1 个答案:

答案 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类的实际实现紧密耦合。理想情况下,您希望测试不需要了解所测试类的内部工作原理。