2012-08-07 113 views
0

我正在爲我的Java應用程序編寫單元測試,我需要爲可能引發的潛在JiBX異常編寫一個測試。我正在測試的方法調用另一個類的方法,其中JiBX異常可能會被拋出。這是我測試的類(姑且稱之爲A類):可以使用Mockito來模擬org.jibx.runtime.BindingDirectory嗎?

@Inject 
private CommonDAL commonDAL; 

@Async 
public Future<String> getTransactionalXXXAvailability(
     List<XXXAvailRequestEntry> requestEntries, TravelWindow travelWindow) { 
    if (requestEntries.size() == 0) 
     return null; 
    XXXAvailRqAccessor requestAccessor = new XXXAvailRequestBuilder().buildRequest(requestEntries, travelWindow); 
    logger.info(requestAccessor.marshalRequest()); 
    String responseAsXml = null; 
    try { 
     responseAsXml = getResponse(requestAccessor.getRequest()); 
    } catch (JiBXException e) { 
     logger.error("Problem unmarshaling the XXX avail response: ", e); 
    } 

    logger.info(responseAsXml); 
    return new AsyncResult<String>(responseAsXml); 
} 

private String getResponse(OTAXXXAvailRQ request) throws JiBXException { 
    HbsiConnectionInfo connectionInfo = new HbsiConnectionInfo(); 
    connectionInfo.useConnectionInfoFromContext(); 

    HBSIXML4OTAWebserviceSoap hbsiSoap = getHbsiSoapService(connectionInfo); 

    InterfacePayload header = new InterfacePayload(); 
    header.setChannelIdentifierId("XXXXXXXXX"); 
    header.setVersion("2005B"); 
    header.setInterface("HBSI XML 4 OTA"); 

    ComponentInfo componentInfo = new ComponentInfo(); 
    XXXAvailRqAccessor requestAccessor = new XXXAvailRqAccessor(request); 
    componentInfo.setId(requestAccessor.getFirstXXXCode()); 
    componentInfo.setUser(connectionInfo.getUsername()); 
    componentInfo.setPwd(connectionInfo.getPassword()); 
    componentInfo.setComponentType(EComponentType.XXX); 

    Login login = new Login(); 
    login.setComponentInfo(componentInfo); 

    Message body = new Message(); 
    // todo: this needs to be unique for every request. 
    // todo: hook up to logging 
    body.setRequestId(UUID.randomUUID().toString()); 
    body.setTransaction(ETransaction.XXX_AVAIL_RQ); 

    body.setXML(requestAccessor.marshalRequest()); 

    return hbsiSoap.getSoapRequest(header, body, login); 
} 

HBSIXML4OTAWebserviceSoap getHbsiSoapService(HbsiConnectionInfo connectionInfo) { 
    HBSIXML4OTAWebservice ws = new HBSIXML4OTAWebservice(connectionInfo.getWsdlLocation()); 

    HBSIXML4OTAWebserviceSoap hbsiSoap = ws.getHBSIXML4OTAWebserviceSoap(); 
    Map<String, Object> requestContext = ((BindingProvider)hbsiSoap).getRequestContext(); 
    String readTimeout = commonDAL.getPropertyValue(new PropertyKey(Section.HBSI, 
      Property.HBSI_WS_READ_TIMEOUT)); 
    requestContext.put(BindingProviderProperties.REQUEST_TIMEOUT, Integer.parseInt(readTimeout)); 
    String connectionTimeout = commonDAL.getPropertyValue(new PropertyKey(Section.HBSI, 
      Property.HBSI_WS_CONNECTION_TIMEOUT)); 
    requestContext.put(BindingProviderProperties.CONNECT_TIMEOUT, Integer.parseInt(connectionTimeout)); 
    return hbsiSoap; 
} 

拋出誤差的方法如下(從另一個類,我們稱之爲B類):

public String marshalRequest() { 
    StringWriter requestAsXml = new StringWriter(); 

    try { 
     IBindingFactory bindingFactory = BindingDirectory.getFactory(PROTECTEDCLASSNAME.class); 
     IMarshallingContext marshalingContext = bindingFactory.createMarshallingContext(); 
     marshalingContext.setIndent(2); 
     marshalingContext.setOutput(requestAsXml); 
     marshalingContext.marshalDocument(request); 

    } catch (JiBXException e) { 
     logger.error("Problem marshaling PROTECTEDCLASSNAME.", e); 
    } 

    return requestAsXml.toString(); 
} 

當「body.setXML(requestAccessor.marshalRequest());」被調用時,另一個類(requestAccessor)被測試訪問,它的方法.marshalRequest是JiBX異常應該被拋出的地方。我正在編寫的測試的目的是讓這個A類的單元測試覆蓋率達到100 &,但是被測系統至少由兩個類組成,因爲我無法模擬名爲requestAccessor的XXXAvailRqAccessor對象。出於以下原因,我無法進行任何測試來產生此錯誤。

  • 稱爲requestAccessor的XXXAvailRqAccessor對象被我測試,所以我不能用一個模擬拋出異常的方法中實例化。

  • 傳遞給.getResponse()的OTAXXXAvailRQ參數不能被模擬,因爲它是由構建器爲XXXAvailRqAccessor創建的。

  • 我試着在IBindingFactory上監聽,但它沒有工作。我在B類中創建了一個實例化IBindingFactory的方法,這樣我就可以窺探它,但那不起作用。

  • 我也嘗試過使用PowerMock在實例化時返回模擬XXXAvailRqAccessor,但是當我試圖模擬JiBXExceptioin for .getRequest時,Mockito說「檢查異常對於此方法無效」。如果我不能讓Mockito拋出這個錯誤,我不知道是否可以操縱關聯的對象來拋出它。

回答

2

好吧不是真的,或者我至少不知道這樣的方式。你可以,如果你真的想這樣做(我反對它)在類中創建一個像這樣的方法:

IBindingFactory getBindingFactory() { 
    return BindingDirectory.getFactory(PROTECTEDCLASSNAME.class); 
} 

而替換該行:

IBindingFactory bindingFactory = BindingDirectory.getFactory(PROTECTEDCLASSNAME.class); 

有了:

IBindingFactory bindingFactory = getBindingFactory(); 

然後你可以探測()(如果你不熟悉它,你可以在文檔中的Mockito.spy()上讀到)這個對象,並使這個方法返回一個模擬。從這一點來看,這是一帆風順的。

這種做法是不勸,但因爲:

  1. 要創建一個新的方法(無用的)只是用於測試
  2. 該方法必須是用於測試可見,所以你不能將其設爲私有...
  3. 我不是間諜一般

問題一個巨大的風扇仍然是:如何正確測試這樣的情況。那麼在大多數情況下,我儘可能地重構,有時候它會有所幫助。而在其他情況下......我還沒有想出更好的解決方案。

+0

其實這是一個很好的解決方案。創建僅用於測試的新方法沒有任何問題。最好有一個很好的可測試的類,而不是少一個方法。新方法必須是包私有的,這非常合適。這種類型的問題是間諜少數真正的好用途之一。還有另一種方法可以做到這一點,您可能更喜歡 - 我會在幾個小時內發佈答案(如果我記得的話)。 – 2012-08-08 02:23:48

1

正如我在評論中所說的,我完全主張Mateusz的解決方案。但有一個選擇。在具有marshalRequest方法的類中,有一個類型爲IBindingFactory的私有最終字段。也在這個類中,有一個包含私有構造函數的額外參數,即IBindingFactory來設置。正常的構造函數將調用BindingDirectory.getFactory(...)然後調用新的構造函數。因此,如果標準構造函數只有一個String參數,則該類可能看起來像這樣。

public class MyClass{ 
    private String name; 
    private IBindingFactory bindingFactory; 

    public MyClass(String name){ 
     this(name, BindingDirectory.getFactory(PROTECTEDCLASSNAME.class)); 
    } 

    MyClass(String name, IBindingFactory bindingFactory){ 
     this.name = name; 
     this.bindingFactory = bindingFactory; 
    } 

    public String marshalRequest() { 
     StringWriter requestAsXml = new StringWriter(); 

     try { 
      IMarshallingContext marshalingContext = bindingFactory.createMarshallingContext(); 
      marshalingContext.setIndent(2); 
      marshalingContext.setOutput(requestAsXml); 
      marshalingContext.marshalDocument(request); 

     } catch (JiBXException e) { 
      logger.error("Problem marshaling PROTECTEDCLASSNAME.", e); 
     } 

     return requestAsXml.toString(); 
    } 
} 

這樣做的原因是這樣的話,在測試你的類,你可以創建一個模擬IBindingFactory,並把它傳遞到包私有構造。

+0

我正在寫的測試是針對一個調用另一個類的'marshalRequest'的類。如果我在'MyClass'中創建一個私有構造函數並且'TestClass'從來沒有調用它,那麼我不能插入一個模擬IBindingFactory作爲正確的參數?除非我可以在'TestClass'中創建一個mockBindingFactory並將其傳遞給'MyClass'構造函數。 – tamuren 2012-08-10 17:11:48

+0

我不太明白。你是說你的被測系統(SUT)由兩個類組成?或者只是調用具有'marshalRequest'方法的類?如果你的SUT沒有包含'marshalRequest'方法,那麼你應該存根整個方法,而不是嘲笑'IBindingFactory'。也許你可以通過發佈你實際嘗試測試的類的代碼來澄清你的需求;那麼我可能會更好地幫助你。 – 2012-08-10 22:33:05

相關問題