Как создать фиктивный объект для Spring WebServiceTemplate? - PullRequest
7 голосов
/ 27 апреля 2009

У меня есть класс, который вызывает существующий веб-сервис. Мой класс правильно обрабатывает допустимые результаты, а также строки ошибок, сгенерированные веб-службой. Основной вызов веб-службы выглядит примерно так (хотя это упрощенно).

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

Теперь мне нужно создать несколько юнит-тестов, которые проверяют все условия успеха и неудачи. Он не может вызвать реальный веб-сервис, поэтому я надеялся, что для клиентской части Spring-WS были доступны фиктивные объекты. Кто-нибудь знает о фиктивных объектах, доступных для WebServiceTemplate или любых связанных классов? Должен ли я просто попытаться написать свой собственный и изменить свой класс для использования интерфейса WebServiceOperations или WebServiceTemplate?

Ответы [ 2 ]

7 голосов
/ 28 апреля 2009

Ответ Майкла очень близок, но вот пример, который работает.

Я уже использую Mockito для своих модульных тестов, поэтому я знаком с библиотекой. Однако, в отличие от моего предыдущего опыта с Mockito, просто издевательство над возвращаемым результатом не помогает. Мне нужно сделать две вещи, чтобы проверить все варианты использования:

  1. Изменить значение, хранящееся в StreamResult.
  2. Создайте исключение SoapFaultClientException.

Во-первых, мне нужно было понять, что я не могу смоделировать WebServiceTemplate с Mockito, поскольку это конкретный класс (вам нужно использовать EasyMock, если это необходимо). К счастью, вызов веб-службы 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;
}

Поддержка второго варианта использования аналогична, но требует исключения. Следующий код создает исключение SoapFaultClientException, которое содержит faultString. Код неисправности используется кодом, который я тестирую и который обрабатывает запрос веб-службы:

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

Для обоих этих вариантов использования может потребоваться больше кода, но они работают для моих целей.

5 голосов
/ 27 апреля 2009

на самом деле я не знаю, существуют ли предварительно настроенные фиктивные объекты, но я сомневаюсь, что они сконфигурированы для всех ваших "условий отказа", поэтому вы можете создать специальный Spring ApplicationContext для вашего теста JUnit с заменой или работать с фиктивным Framework, это не так сложно: -)

Я использовал Mockito Mock Framework для примера (и набрал его быстро), но EasyMock или предпочитаемый вами mock Framework также должны это делать

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