2011-12-28 275 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