Как создать макет объекта для Spring WebServiceTemplate?
Вопрос
У меня есть класс, который обращается к существующей веб-службе.Мой класс правильно обрабатывает действительные результаты, а также строки ошибок, сгенерированные веб-службой.Базовый вызов веб-сервиса выглядит примерно так (хотя и упрощенно).
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, а не интерфейс WebServiceOperations?Шаблон веб-службы?
Решение
Ответ Майкла очень близок, но вот пример, который работает.
Я уже использую Mockito для своих модульных тестов, поэтому знаком с библиотекой.Однако, в отличие от моего предыдущего опыта работы с Mockito, простое издевательство над возвращаемым результатом не помогает.Мне нужно сделать две вещи, чтобы протестировать все варианты использования:
- Измените значение, хранящееся в StreamResult.
- Вызовите исключение 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, содержащее errorString.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;
}
Для обоих этих вариантов использования может потребоваться больше кода, но для моих целей они подходят.
Другие советы
на самом деле я не знаю, существуют ли предварительно настроенные макетные объекты, но я сомневаюсь, что они настроены для всех ваших «условий сбоя», поэтому вы можете создать специальный Spring ApplicationContext для вашего теста JUnit с заменой или работать с макетом Framework, это не так уж и сложно :-)
я использовал Мокито 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;
}
}