2011-12-28 201 views
6

我在我的spring应用程序中为服务层编写单元测试。
这里是我的服务类使用Mockito进行单元测试

@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,我需要在调用测试方法之前明确设置,否则它给出nullPointerExceptionrequestDaonullStubRequestService.getRequest方法。
  3. 当单元测试Spring服务层时,哪些是最佳实践? (如果我做错了什么)。
+0

如果你改变你的问题给出的答案之后,答案就没有多大意义了。我会回滚你最后的编辑。 – 2011-12-28 12:53:17

+0

@JB:道歉编辑问题。我只是想提供正确和准确的信息。谢谢 – xyz 2011-12-28 12:54:19

回答

3
  1. 如果你真的觉得它会让你的测试更容易理解 - 你可以初始化一个spring context并从那里获取所有的对象。但是,通常需要为测试专门创建一个单独的弹簧配置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需要从对象嘲笑向上DataAccessLayer流程它正确,并将其发送到嘲笑向上ViewLayer
+0

是否有具体的原因使用不同的上下文文件来实例化测试中的bean?并感谢例如。它确实帮了很大忙。 – xyz 2011-12-28 12:56:02

+0

没有理由,只是它通常与您的测试不兼容。例如,它需要一些JNDI资源,可能会加载一些数据库(并且测试从不使用它们),也许会有一些安全性。因此,最终你会注意到为测试创建单独的上下文文件更容易。 – bezmax 2011-12-28 12:58:07

+0

是的,有一个原因:你不想用真正的DAO测试服务。你想要一个模拟的DAO来测试这个服务。但请按照Max的建议和我的意见:不要使用Spring上下文来单元测试服务。您可能希望Spring上下文在DAO测试中注入数据源,SessionFactory和TxManager,但不在服务测试中。 – 2011-12-28 13:00:25

7

你的测试没问题。它甚至不必具有@ContextConfiguration注释。

像Spring这样的依赖注入框架的整个意义在于能够通过简单实例化它们,设置模拟依赖关系,然后调用它们的方法来单元测试服务。

你正确地做到了。你不需要有这样的单元测试的Spring上下文。这就是为什么他们被称为单元测试的原因:他们测试了它的所有实际依赖性,包括Spring。

备注:假设您使用的是JUnit,则应交换assertXxx方法的参数。预期值在实际值之前。当断言失败并且你有一个像“期待6但是3”而不是“期待3,但是6”的信息时,这变得很重要。

+0

感谢您的回答和建议。是否意味着我们应该明确地创建服务对象并设置所有自动装配的依赖关系?我被建议autowire它而不是手动设置它。 – xyz 2011-12-28 12:51:40

+1

运行应用程序时自动装配正常。在进行单元测试时并不需要,甚至不需要,因为每个测试都需要注入自己的模拟依赖。 – 2011-12-28 12:55:42