9

我有一个调用现有 Web 服务的类。我的班级正确处理了有效结果以及 Web 服务生成的错误字符串。对 Web 服务的基本调用看起来像这样(尽管这是简化的)。

public String callWebService(final String inputXml)
{
  String result = null;

  try
  {
    StreamSource input = new StreamSource(new StringReader(inputXml));
    StringWriter output = new StringWriter();

    _webServiceTemplate.sendSourceAndReceiveToResult(_serviceUri, input, new StreamResult(output));

    result = output.toString();
  }
  catch (SoapFaultClientException ex)
  {
    result = ex.getFaultStringOrReason();
  }

  return result;
}

现在我需要创建一些单元测试来测试所有的成功和失败条件。它不能调用实际的 Web 服务,所以我希望 Spring-WS 的客户端有可用的模拟对象。有谁知道可用于 WebServiceTemplate 或任何相关类的模拟对象?我是否应该尝试编写自己的类并修改我的类以使用 WebServiceOperations 接口与 WebServiceTemplate?

4

2 回答 2

10

迈克尔的回答非常接近,但这是一个有效的例子。

我已经在我的单元测试中使用了 Mockito,所以我对这个库很熟悉。然而,与我之前使用 Mockito 的经验不同,简单地模拟返回结果并没有帮助。我需要做两件事来测试所有用例:

  1. 修改 StreamResult 中存储的值。
  2. 引发 SoapFaultClientException。

首先,我需要意识到我不能用 Mockito 模拟 WebServiceTemplate,因为它是一个具体的类(如果这是必要的,你需要使用 EasyMock)。幸运的是,对 Web 服务 sendSourceAndReceiveToResult 的调用是 WebServiceOperations 接口的一部分。这需要更改我的代码以期望 WebServiceOperations 与 WebServiceTemplate。

以下代码支持在 StreamResult 参数中返回结果的第一个用例:

private WebServiceOperations getMockWebServiceOperations(final String resultXml)
{
  WebServiceOperations mockObj = Mockito.mock(WebServiceOperations.class);

  doAnswer(new Answer()
  {
    public Object answer(InvocationOnMock invocation)
    {
      try
      {
        Object[] args = invocation.getArguments();
        StreamResult result = (StreamResult)args[2];
        Writer output = result.getWriter();
        output.write(resultXml);
      }
      catch (IOException e)
      {
        e.printStackTrace();
      }

      return null;
    }
  }).when(mockObj).sendSourceAndReceiveToResult(anyString(), any(StreamSource.class), any(StreamResult.class));

  return mockObj;
}

对第二个用例的支持类似,但需要抛出异常。以下代码创建了一个包含 faultString 的 SoapFaultClientException。faultCode 由我正在测试的处理 Web 服务请求的代码使用:

private WebServiceOperations getMockWebServiceOperations(final String faultString)
{
  WebServiceOperations mockObj = Mockito.mock(WebServiceOperations.class);

  SoapFault soapFault = Mockito.mock(SoapFault.class);
  when(soapFault.getFaultStringOrReason()).thenReturn(faultString);

  SoapBody soapBody = Mockito.mock(SoapBody.class);
  when(soapBody.getFault()).thenReturn(soapFault);

  SoapMessage soapMsg = Mockito.mock(SoapMessage.class);
  when(soapMsg.getSoapBody()).thenReturn(soapBody);

  doThrow(new SoapFaultClientException(soapMsg)).when(mockObj).sendSourceAndReceiveToResult(anyString(), any(StreamSource.class), any(StreamResult.class));

  return mockObj;
}

这两个用例可能需要更多代码,但它们适用于我的目的。

于 2009-04-28T15:46:18.040 回答
5

实际上我不知道是否存在预配置的模拟对象,但我怀疑是否为所有“失败条件”进行了配置,因此您可以为您的 JUnit 测试创建一个特殊的 Spring ApplicationContext 替代品或使用模拟框架,它是没那么难:-)

我在示例中使用了Mockito模拟框架(并快速输入),但EasyMock或您喜欢的模拟框架也应该这样做

package org.foo.bar
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

public class WebserviceTemplateMockTest {

    private WhateverTheInterfaceIs webServiceTemplate;
    private TestClassInterface testClass;
    private final String inputXml = "bar";


    @Test
    public void testClient(){
        // 
        assertTrue("foo".equals(testClass.callWebService(inputXml));
    }

    /**
     * Create Webservice Mock.
     */
    @Before
    public void createMock() {
        // create Mock
        webServiceTemplate = mock(WhateverTheInterfaceIs.class);
        // like inputXml you need to create testData for Uri etc.
        // 'result' should be the needed result data to produce the
        // real result of testClass.callWebService(...)
        when(webServiceTemplate.sendSourceAndReceiveToResult(Uri, inputXml, new StreamResult(output))).thenReturn(result);
        // or return other things, e.g.
        // .thenThrow(new FoobarException());
        // see mockito documentation for more possibilities
        // Setup Testclass
        TestClassImpl temp = new TestClassImpl();
        temp.setWebServiceTemplate(generatedClient);
        testClass = temp;
    }
}
于 2009-04-27T19:45:51.717 回答