0

我正在为我的 Java 应用程序编写单元测试,我需要为可能引发的潜在 JiBX 异常编写测试。我正在测试的方法调用来自另一个类的方法,其中可能会引发 JiBX 异常。这是我正在测试的类(我们称之为 A 类):

@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,但是当我尝试为 .getRequest 模拟 JiBXExceptioin 时,Mockito 说“检查的异常对于此方法无效”。如果我不能让 Mockito 抛出这个错误,我不知道是否可以操纵关联的对象来抛出它。

4

2 回答 2

2

不是真的,或者我至少不知道这种方式。如果您真的想这样做(我反对),您可以在该类中创建这样的方法:

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

并替换这一行:

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

和:

IBindingFactory bindingFactory = getBindingFactory();

然后你可以 spy() (如果你不熟悉它,你可以阅读文档中的 Mockito.spy() )这个对象并使这个方法返回一个模拟。从那时起,它是一帆风顺的。

但不建议使用这种方法,因为:

  1. 您正在创建一种新方法(无用的方法)只是为了测试
  2. 所述方法必须对测试可见,因此您不能将其标记为私有...
  3. 总的来说,我不是间谍的忠实粉丝

问题仍然存在:如何正确测试此类案例。好吧,在大多数情况下,我会尝试尽可能多地进行重构,有时它会有所帮助。在其他情况下......好吧,我仍然没有想出更好的解决方案。

于 2012-08-07T13:09:00.717 回答
1

正如我在评论中所说,我完全支持 Mateusz 的解决方案。但有一个替代方案。在具有该marshalRequest方法的类中,有一个类型为 的私有 final 字段IBindingFactory。同样在这个类中,有一个带有一个额外参数的包私有构造函数,即IBindingFactoryto 设置。普通的构造函数将调用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();
    }
}

这样做的原因是,在您的类的测试中,您可以创建一个 mockIBindingFactory并将其传递给 package-private 构造函数。

于 2012-08-08T11:15:27.367 回答