java 如何为 Spring 的 WebServiceTemplate 创建模拟对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/794899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 13:49:48  来源:igfitidea点击:

How do I create a mock object for Spring's WebServiceTemplate?

javamockingspring-ws

提问by Kevin

I have a class which calls out to an existing web service. My class properly handles valid results as well as fault strings generated by the web service. The basic call to the web service looks something like this (although this is simplified).

我有一个调用现有 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;
}

Now I need to create some unit tests which test all of the success and failure conditions. It cannot call the actual web service, so I was hoping there were mock objects available for the client side of Spring-WS. Does anyone know of an mock objects available for the WebServiceTemplate or any related classes? Should I just attempt to write my own and modify my class to use the WebServiceOperations interface vs. WebServiceTemplate?

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

回答by Kevin

Michael's answer is very close, but here is the example that works.

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

I already use Mockito for my unit tests, so I am familiar with the library. However, unlike my previous experience with Mockito, simply mocking the return result does not help. I need to do two things to test all of the use cases:

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

  1. Modify the value stored in the StreamResult.
  2. Throw a SoapFaultClientException.
  1. 修改存储在 StreamResult 中的值。
  2. 抛出 SoapFaultClientException。

First, I needed to realize that I cannot mock WebServiceTemplate with Mockito since it is a concrete class (you need to use EasyMock if this is essential). Luckily, the call to the web service, sendSourceAndReceiveToResult, is part of the WebServiceOperations interface. This required a change to my code to expect a WebServiceOperations vs a WebServiceTemplate.

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

The following code supports the first use case where a result is returned in the StreamResult parameter:

以下代码支持在 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;
}

The support for the second use case is similar, but requires the throwing of an exception. The following code creates a SoapFaultClientException which contains the faultString. The faultCode is used by the code I am testing which handles the web service request:

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

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;
}

More code may be required for both of these use cases, but they work for my purposes.

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

回答by Michael Pralow

actually i don't know if there exist preconfigured Mock Objects, but i doubt there are configured for all your "failure Conditions", so you can create a special Spring ApplicationContext for your JUnit Test with a substitute or work with a mock Framework, it's not that hard :-)

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

i used the MockitoMock Framework for the example (and typed it quickly), but EasyMockor your preferred mock framework should do it as well

我使用MockitoMock 框架作为示例(并快速输入),但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;
    }
}