Статьи

Разработка управляемой тестом клиентской веб-службы

Эта статья о тестовой разработке клиентов веб-сервисов. Поскольку мы будем говорить о модульных тестах, функциональных тестах, разработке через тестирование и о другой теме, связанной с тестированием, я хотел бы сначала уточнить терминологию.

Начнем с юнит-тестов. Модульный тест — это автоматический тест, который тестирует только одну небольшую часть нашего приложения в изоляции. Модульный тест не должен взаимодействовать с файловой системой, базой данных или сторонней библиотекой. Зачем? Главным образом потому, что мы хотим протестировать только код и больше ничего. Мы также хотим выполнить тест как можно быстрее без каких-либо проблем с настройкой.

Иногда нам нужно тестировать больше, чем просто код. Иногда необходимо проверить конфигурацию или взаимодействие с библиотекой ORM. Иногда мы хотим протестировать некоторый уровень приложения, который не легко проверить. Мы должны помнить, что в обычном корпоративном приложении код — это только одна часть всей системы. У нас обычно есть некоторые файлы конфигурации XML, аннотации, страницы JSP и так далее. Эти детали тоже должны быть проверены.

И это то, для чего предназначены функциональные тесты. Функциональный тест — это автоматический тест, который тестирует больше частей приложения вместе. Существует целый спектр функциональных тестов. С одной стороны, это может быть простой тест, который выглядит как модульный тест, но он тестирует несколько классов за один раз. С другой стороны, это может быть большой сложный тест, который тестирует вместе все уровни приложения — от JSP до базы данных. Очевидно, что функциональные тесты медленнее, они не сообщают нам, где находится ошибка, и они более подвержены ошибкам конфигурации.

Разработка через тестирование — метод, который можно использовать при разработке новых функций. Сначала пишется провальный тест, затем добавляется новая функциональность, обеспечивающая прохождение теста. Какие виды тестов следует использовать для разработки через тестирование (TDD)? Модульные или функциональные тесты? Обычно используются юнит-тесты. Это имеет смысл. TDD должен заставить нас писать лучший код, а модульные тесты связаны с кодом. Но бывают случаи, когда имеет смысл использовать функциональные тесты для TDD. Одним из примеров является уровень доступа к данным, но сегодня я хочу написать о клиентах WS, поэтому сосредоточусь на них.

Классический уровень клиентского доступа WS выглядит следующим образом (псевдокод)

ReturnValue wsClientMethod(inputData)
{
request = convertInputDataToRequest(inputData);
response = callService(request);
return convertResponseToReturnValue(response);
}

Прежде всего мы должны преобразовать входные данные в запрос. Здесь мы можем либо генерировать XML напрямую, либо мы можем использовать инструмент отображения объектов / XML. Затем мы должны вызвать службу и преобразовать ответ обратно в некоторые значения, которые ожидаются в качестве возвращаемого значения.

Теперь давайте представим, как будет выглядеть юнит-тест. Тесты методов преобразования были бы очень скучными. Сам метод конвертера обычно представляет собой ужасную кучу методов множества. Вы знаете, что-то вроде «получить имя от этого компонента и установить его для этого компонента, взять адрес от первого компонента и установить его для другого, …». Тест был бы очень похож по уродству. Я могу написать такой код один раз, но никто не заставит меня написать его дважды.

Давайте подумаем, как сделать это лучше. Если вы используете Spring WS , вы можете использовать тестовую библиотеку Spring WS, чтобы сделать вашу жизнь проще.

Мы можем начать со следующего теста.

@Test
public void testGetFligts()
{

//create control
WsMockControl mockControl = new WsMockControl();
//teach mock what to do and create it
WebServiceMessageSender mockMessageSender = mockControl.expectRequest("PRG-DUB-request.xml").createMock();
webServiceTemplate.setMessageSender(mockMessageSender);

client.getFlights("PRG","DUB");

mockControl.verify();
}

Здесь мы тестируем метод получения полетов. Прежде всего создается макет. Макет ожидает, что сервис будет вызываться с запросом, указанным в XML-файле. Если служба не вызывается или запрос отличается, тест не пройден. Обратите внимание, что нам не нужно реализовывать скучное сравнение запроса. Это делается автоматически на основе файла XML. Тест не только проще, но и полезнее. Он также проверяет, что правильный XML генерируется. Признаюсь, это не юнит тест. Он тестирует третью библиотеку (отображение XML) и обращается к файловой системе. Но это довольно быстро и не требуется много настроек. Он сочетает в себе скорость юнит-тестов с полезностью функциональных тестов.

Чтобы выполнить предыдущий тест, мы должны реализовать преобразование запроса и вызов WS. Но это еще не все, мы должны смоделировать ответ сервера и проверить его преобразование. Это довольно просто.

@Test
public void testGetFligts()
{

//create control
WsMockControl mockControl = new WsMockControl();
//teach mock what to do and create it
WebServiceMessageSender mockMessageSender = mockControl.expectRequest("PRG-DUB-request.xml")
.returnResponse("PRG-DUB-response.xml").createMock();
webServiceTemplate.setMessageSender(mockMessageSender);

//do the test
List<Flight> flights = client.getFlights("PRG","DUB");
assertNotNull(flights);
assertEquals(1, flights.size());
assertEquals("PRG",flights.get(0).getFrom().getCode());
assertEquals("DUB",flights.get(0).getTo().getCode());

//verify that everything was called at least once
mockControl.verify();
}

Разница лишь в том, что нам нужно было добавить отклик на макет и сравнить результат с ожиданиями. Ложный ответ снова указывается в виде XML-файла, так что это довольно интуитивно понятно. Полный код доступен здесь .

Написание теста таким способом довольно легко и быстро. Также более очевидно, чего пытается достичь тест. Вы можете сосредоточиться на функциональности, вы можете подумать о возможных комбинациях запрос / ответ. Если вы хотите попробовать, вы можете скачать инструмент здесь .