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

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

已下线请稍等 发布于 2021-11-25 字数 1172 浏览 639 回复 2 原文

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).

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?

如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。

扫码加入群聊

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

聚集的泪 2022-06-07 2 楼

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 :-)

i used the Mockito Mock Framework for the example (and typed it quickly), but EasyMock or your preferred mock framework should do it as well

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;
    }
}
夜灵血窟げ 2022-06-07 1 楼

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:

  1. Modify the value stored in the StreamResult.
  2. Throw a 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.

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

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:

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.