【问题标题】:Unit testing with Mockito使用 Mockito 进行单元测试
【发布时间】:2011-12-28 12:24:27
【问题描述】:

我正在为我的 Spring 应用程序中的服务层编写单元测试。
这是我的服务类

    @Service
    public class StubRequestService implements RequestService {    
        @Autowired
        private RequestDao requestDao;  

        @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
        @Override
        public Request getRequest(Long RequestId) {
            Request dataRequest = requestDao.find(requestId);
            return dataRequest;
        }
    }  

这是我的测试课

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml" })
public class StubRequestServiceTest {

    @Mock
    public RequestDao requestDao;

    StubRequestService stubRequestService;  // How can we Autowire this ?

    @org.junit.Before
    public void init() {
      stubRequestService = new StubRequestService();  // to avoid this 
      stubRequestService.setRequestDao(dataRequestDao);  
      // Is it necessary to explicitly set all autowired elements ?  
      // If I comment/remove above setter then I get nullPointerException 
    }

    @Test
    public void testGetRequest()  {
        Request request = new Request();
        request.setPatientCnt("3");
        when(requestDao.find(anyLong())).thenReturn(request);
        assertEquals(stubRequestService.getRequest(1234L).getPatientCnt(),3);
    }    
}   

它工作正常,但我有几个问题

  1. 我们如何在测试中Autowire 服务类?我在init() 方法中使用构造函数来创建服务对象。
  2. 我们是否必须为服务类设置所有Autowire 元素?例如 StubRequestService 已自动连接 RequestDao ,我需要在调用测试方法之前明确设置它,否则它会在 requestDao 中给出 nullPointerException requestDaonullStubRequestService.getRequest 方法中。
  3. 在对 Spring 服务层进行单元测试时应遵循哪些良好实践? (如果我做错了什么)。

【问题讨论】:

  • 如果你在给出答案后改变了你的问题,那么答案就没有多大意义了。我会回滚你上次的编辑。
  • @JB:抱歉编辑问题。我只是想提供正确和准确的信息。谢谢

标签: spring unit-testing mockito


【解决方案1】:

你的测试很好。它甚至不必有@ContextConfiguration 注释。

像 Spring 这样的依赖注入框架的全部意义在于能够通过简单地实例化服务、设置模拟依赖项,然后调用它们的方法来对服务进行单元测试。

你做得对。您不需要为此类单元测试提供 Spring 上下文。这就是它们被称为单元测试的原因:它们在隔离所有实际依赖项(包括 Spring)的情况下对其进行测试。

旁注:假设您使用的是 JUnit,assertXxx 方法的参数应该交换。期望值先于实际值。当断言失败并且您收到诸如“期望 6 但为 3”而不是“期望 3 但为 6”之类的消息时,这一点变得很重要。

【讨论】:

  • 感谢您的回答和建议。这是否意味着我们应该显式创建服务对象并设置所有自动装配的依赖项?我被建议自动连接它而不是手动设置它。
  • 运行应用程序时自动装配正常。单元测试时不需要,甚至不需要,因为每个测试都希望注入自己的模拟依赖项。
【解决方案2】:
  1. 如果你真的觉得它会让你的测试更容易理解 - 你可以初始化一个 spring 上下文并从那里获取所有对象。但是,通常它需要专门为测试创建一个单独的 spring 配置 XML 文件,因此我不推荐它。

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
    stubRequestService = (RequestService)applicationContext.getBean("myRequestServiceBean");
    
  2. (和 3)基本上,我更喜欢完全隔离地测试我的应用程序的每个组件,这就是为什么我不推荐我在 [1] 中描述的内容。

这意味着,您需要对应用程序进行单独的逻辑切片并对其进行测试,同时完全模拟它尝试访问的所有内容。

假设你有三个类:

//Fetches stuff from some webservice and converts to your app domain POJOs
class DataAccessLayer {
    public void setWebservice(Webservice ws) {...};

    public MyObject getMyObject() {...};
}

//Formats the domain POJOs and sends them to some kind of outputstream or stuff.
class ViewLayer {
    public void setOutputStream(OutputStream os) {...};

    public void viewMyObject(MyObject mo) {...};
}

//Main entry point of our MyObject fetch-process-display workflow
class Controller {
    public void setDataAccessLayer(DataAccessLayer dal) {...};
    public void setViewLayer(ViewLayer vl) {...};

    public void showMyObject() {
        MyObject mo = dal.getMyObject();
        ...some processing here maybe...
        vl.viewMyObject(mo);
    }
}

现在,我们可以在这里写什么测试?

  1. 测试DataAccessLayer 是否正确地将对象从模拟 WS 转换为我们的域对象。
  2. 测试ViewLayer 是否正确格式化给他的对象并将其写入模拟输出流。
  3. 测试 Controller 是否从 mocked 获取对象 DataAccessLayer 正确处理它并将其发送到 mocked ViewLayer

【讨论】:

  • 是否有特定的理由使用不同的上下文文件来实例化测试中的 bean?并感谢例如。这真的很有帮助。
  • 没有原因,只是经常发生它与您的测试不兼容。例如,它需要一些 JNDI 资源,可能会加载一些数据库(并且测试从不使用它们),可能还有一些安全性。所以最后你会开始注意到创建一个单独的上下文文件进行测试更容易。
  • 是的,有一个原因:您不想使用真正的 DAO 测试服务。你想要一个模拟 DAO 来测试服务。但请遵循 Max 的建议和我的建议:不要使用 Spring 上下文对服务进行单元测试。您可能希望 Spring 上下文在 DAO 测试中注入 Datasource、SessionFactory 和 TxManager,而不是在服务测试中。
  • @JBNizet 好吧,有时在测试中使用 spring 上下文是值得的。例如,当您对某些依赖于某些特定配置的大型组件进行集成测试时。
  • @Max:是的,这就是我在之前评论中的建议。它在测试 DAO 时很有用,例如,它依赖于数据库、TxManager 等。对于纯业务服务,它不应该是必需的。
【解决方案3】:

或者你可以使用 springockito https://bitbucket.org/kubek2k/springockito/wiki/Home,它会让你的测试更干净

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 1970-01-01
    • 2017-11-22
    • 2017-09-19
    • 2019-04-27
    • 1970-01-01
    相关资源
    最近更新 更多