Сквозное тестирование веб-сервисов
Вопрос
впервые постер и сторонник TDD.:-) Я буду немного многословен, так что, пожалуйста, потерпите.
Недавно я начал разрабатывать веб-сервисы на основе SOAP, используя среду Apache CXF, Spring и Commons Chain для реализации бизнес-потоков.Проблема, с которой я здесь сталкиваюсь, связана с тестированием веб-сервисов - тестированием, как при модульном тестировании, так и при функциональном тестировании.
Моя первая попытка модульного тестирования закончилась полным провалом.Чтобы обеспечить гибкость модульных тестов, я использовал XML-файл Spring для хранения тестовых данных.Кроме того, вместо того, чтобы создавать экземпляры «компонентов» для тестирования, я извлек их из контекста приложения Spring.Файлы XML, содержащие данные, быстро вышли из-под контроля;создание графов объектов в XML оказалось кошмаром.Поскольку «компоненты», подлежащие тестированию, были выбраны из контекста приложения Spring, каждый запуск теста загружался все компоненты, задействованные в моем приложении, используемые объекты DAO и т. д.Кроме того, в отличие от концепции централизованного или сосредоточенного на тестировании только компонента модульного тестирования, мои модульные тесты начали затрагивать базы данных, взаимодействовать с почтовыми серверами и т. д.Плохо, очень плохо.
Я понял, что сделал не так, и начал думать, как это исправить.Следуя совету из одного из постов на этой доске, я нашел Mockito, фреймворк для имитации Java, чтобы можно было отказаться от использования реальных классов DAO и почтовых серверов и просто поглумиться над функциональностью.
Поскольку модульные тесты немного под контролем, это подводит меня ко второй проблеме;зависимость от данных.Веб-сервисы, которые я разрабатываю, имеют очень мало логики, но сильно зависят от данных.В качестве примера рассмотрим один из моих компонентов:
public class PaymentScheduleRetrievalComponent implements Command {
public boolean execute(Context ctx) {
Policy policy = (Policy)ctx.get("POLICY");
List<PaymentSchedule> list = billingDAO.getPaymentStatementForPolicy(policy);
ctx.put("PAYMENT_SCHEDULE_LIST", list);
return false;
}
}
Большинство моих компонентов следуют одному и тому же маршруту — выбирают объект домена из контекста, обращаются к DAO [здесь мы используем iBatis в качестве преобразователя SQL] и получаем результат.
Итак, теперь вопросы:
- Как проверяются классы DAO, особенно если одна вставка или обновление может оставить базу данных в «нестабильном» состоянии [в случаях, когда, скажем, 3 вставки в разные таблицы фактически образуют одну транзакцию]?
- Каков стандарт де-факто для веб-сервисов функционального тестирования, которые перемещают большой объем данных, т.е.бессмысленные вставки/извлечения из хранилища данных?
Ваш личный опыт/комментарии будут очень признательны.Пожалуйста, дайте мне знать, если я упустил некоторые детали с моей стороны при объяснении возникшей проблемы.
-Саске
Решение
Я бы порекомендовал базу данных в памяти для запуска модульных тестов, например HSQL.Вы можете использовать это для создания схемы на лету (например, если вы используете Hibernate, вы можете использовать файлы сопоставлений XML), а затем вставлять/обновлять/удалять по мере необходимости перед уничтожением базы данных в конце модульного теста.Ваш тест ни при каких обстоятельствах не повлияет на вашу реальную базу данных.
Что касается второй проблемы (сквозное тестирование веб-сервисов): в прошлом я успешно тестировал сервисы на основе CXF.Хитрость заключается в том, чтобы опубликовать ваш веб-сервис с помощью легкого веб-сервера в начале вашего теста (Jetty идеален), затем использовать CXF, чтобы указать клиенту конечную точку вашего веб-сервиса, выполнить вызовы и, наконец, закрыть Jetty. экземпляр, на котором будет размещен ваш веб-сервис после завершения модульного теста.
Чтобы добиться этого, вы можете использовать классы JaxWsServerFactoryBean (на стороне сервера) и JaxWsProxyFactoryBean (на стороне клиента), поставляемые с CXF, пример кода см. на этой странице:
Я бы также высоко оценил пользовательский интерфейс SOAP для функционального тестирования вашего веб-сервиса.JMeter также чрезвычайно полезен для стресс-тестирования веб-сервисов, что особенно важно для тех сервисов, которые выполняют поиск в базе данных.
Другие советы
На вашем месте я бы держался подальше от шаблона «Контекст как глобальная хэш-карта».
Похоже, вы тестируете свое отображение персистентности...
Возможно, вы захотите взглянуть на: тестирование постоянных объектов без пружины
Прежде всего:Есть ли причина, по которой вам нужно получить тестируемый объект (SUT) из контекста приложения Spring?Для эффективного модульного тестирования вы должны иметь возможность создавать SUT без контекста.Похоже, у вас где-то есть какие-то скрытые зависимости.Это может быть причиной вашей головной боли.
Как классы DAO тестируются ESP, когда одна вставка или обновление могут оставить базу данных в «нестабильном» состоянии [в тех случаях, когда скажем, 3 вставки в разные таблицы фактически образуют одну транзакцию]?
Кажется, вас беспокоит целостность базы данных после запуска тестов.Если возможно, используйте для тестирования собственную базу данных, где вам не нужно об этом заботиться.Если у вас есть такая база данных-песочницы, вы можете удалять данные по своему усмотрению.В этом случае я бы сделал следующее:
- Помечайте все свои поддельные данные каким-нибудь общим идентификатором, например, добавляя к полю специальный префикс.
- Перед запуском теста добавьте оператор удаления, который удаляет помеченные данные.Если его нет, то ничего страшного не происходит.
- Запустите одиночный тест DAO.После этого повторите шаг 2.для следующего теста.
Каков стандарт DE-FACTO для веб-сервисов функционального тестирования, которые перемещаются вокруг множества данных, т.е.Бессмысленные вставки/поиск из хранилища данных?
Я ничего не знаю.Из вопроса, который вы задаете, я могу сделать вывод, что с одной стороны у вас есть веб-сервис, а с другой — база данных.Разделите обязанности.Имейте отдельные наборы тестов для каждой стороны.Одна сторона просто тестирует доступ к базе данных (как описано выше).С другой стороны, просто тестируем запросы и ответы веб-сервисов.В этом случае платит заглушка/подделка/макет уровня, обращающегося к сети.Или рассмотрим https://wsunit.dev.java.net/.
Если программа только вводит и выводит данные, я думаю, что поведение невелико.Если это так, то самая сложная работа — это модульное тестирование стороны базы данных и стороны веб-сервиса.Дело в том, что вы можете проводить модульное тестирование без необходимости получения «реалистичных» данных.Для функционального тестирования вам потребуются данные, собранные вручную и приближенные к реальности.Это может быть обременительно, но если вы уже интенсивно тестировали части базы данных и веб-сервисов, это должно значительно снизить потребность в «реалистичных» тестовых примерах.
Прежде всего, проясните ситуацию.
В идеальном мире жизненный цикл создаваемого вами программного обеспечения выглядит примерно так:- SY делает отчет с клиентом, поэтому вы получили пользовательную историю с примерами о том, как должно работать приложение - вы обобщаете историю пользователя, поэтому вы получили правила, которые вы называете в качестве вариантов использования - вы начинаете писать часть функциональной (конец к концу) тест, и он не удается ...- после этого вы создаете пользовательский интерфейс и макетируете сервисы, чтобы получить зеленый функциональный тест и спецификацию того, как ваши сервисы должны работать...- Ваша задача состоит в том, чтобы сохранить функциональный тест зеленым, и реализовать услуги пошаговые тесты интеграции и высмеивать зависимости с тем же подходом, пока не достигнете уровня модульных тестов - после этого вы выполняете следующую итерацию со сценариями использования , напишите следующий функциональный тест, и так далее до конца проекта - после этого вы проведете приемные тесты с клиентом, который принимает продукт и много платит
Итак, что мы из этого узнали:
- Существует множество типов тестов (не путайте их друг с другом)
- функциональные тесты — для тестирования вариантов использования (ничего не имитировать)
- интеграционные тесты — для тестирования взаимодействия приложения, компонента, модуля, класса (макет ненужных компонентов)
- юнит-тесты — для тестирования одного класса изолированно от его окружения (смоки всего)
- пользовательские приемочные тесты - клиент удостоверяется, что он принимает продукт (ручные функциональные тесты или презентация, сделанная на основе автоматических функциональных тестов в работе)
- Не нужно тестировать все функциональными тестами и интеграционными тестами, потому что это невозможно.Тестируйте только нужную часть с помощью функциональных и интеграционных тестов и тестируйте все с помощью модульных тестов!Ознакомьтесь с пирамида тестирования.
- Используйте TDD, это облегчает жизнь!
- Как классы DAO тестируются ESP, когда одна вставка или обновление могут оставить базу данных в «нестабильном» состоянии [в тех случаях, когда скажем, 3 вставки в разные таблицы фактически образуют одну транзакцию]?
Вам не нужно тестировать транзакции базы данных.Предположим, что они работают хорошо, потому что разработчики баз данных их уже протестировали, и я уверен, что вы не хотите писать тесты параллелизма...БД — это внешний компонент, поэтому вам не нужно тестировать его самостоятельно.Вы можете написать уровень доступа к данным, чтобы адаптировать хранилище данных к вашей системе, и написать интеграционные тесты только для этих адаптеров.В случае миграции базы данных эти тесты будут работать и с адаптерами новой базы данных, поскольку вы пишете их для реализации определенного интерфейса...Любыми другими тестами (кроме функциональных тестов) вы можете макетировать свой уровень доступа к данным.Сделайте то же самое со всеми остальными внешними компонентами, напишите адаптеры и макетируйте их.Поместите эти виды интеграционных тестов в другой набор тестов, чем другие тесты, поскольку они медленны из-за доступа к базе данных, доступа к файловой системе и т. д.
- Каков стандарт де-факто для веб-сервисов функционального тестирования, которые перемещают большой объем данных, т.е.Бессмысленные вставки/поиск из хранилища данных?
Вы можете макетировать свое хранилище данных с помощью базы данных в памяти, которая реализует те же адаптеры хранения, пока вы не реализуете все остальное, кроме базы данных.После этого вы реализуете уровень доступа к данным для базы данных и также тестируете его с помощью функциональных тестов.Это будет медленно, но его нужно запускать только один раз, например, в каждом новом выпуске...Если при разработке вам нужны функциональные тесты, вы можете снова смоделировать их с помощью решения в памяти...Альтернативный подход для запуска только затронутых функциональных тестов путем разработки или изменения настроек тестовой базы данных, чтобы ускорить работу, и так далее...Я уверен, что существует множество решений по оптимизации тестирования...
Я должен сказать, что не очень понимаю вашу проблему.Является ли проблема, что ваша база данных остается в измененном состоянии после того, как вы запустили тест?
Да, на самом деле здесь есть две проблемы.Первая из них — проблема с базой данных, оставшейся в несогласованном состоянии после выполнения тестовых примеров.Во-вторых, я ищу элегантное решение с точки зрения сквозного тестирования веб-сервисов.
Для эффективного модульного тестирования вы должны иметь возможность создавать SUT без контекста.Похоже, у вас где -то есть скрытые зависимости.Это может быть корнем от вашей головной боли.
Это действительно было основной причиной моих головных болей, от которых я сейчас собираюсь избавиться с помощью насмешливой структуры.
Похоже, вы беспокоитесь о заполненности базы данных после того, как вы запустили тесты.Если возможно, используйте собственную базу данных для тестирования, где вам не нужно заботиться об этом.Если у вас есть такая база данных песочницы, вы можете удалить данные по своему желанию.
Это действительно одно из решений проблемы, о которой я упоминал в своем предыдущем посте, но это может работать не во всех случаях, особенно при интеграции с устаревшей системой, в которой база данных/данные не находятся под вашим контролем, а также в тех случаях, когда некоторые DAO методы требуют, чтобы определенные данные уже присутствовали в данном наборе таблиц.Стоит ли мне изучить среды модульного тестирования баз данных, такие как DBUnit?
В этом случае он оплачивает заглушку/подделку/издеваться над уровнем, разговаривающим с сетью.Или рассмотрим https://wsunit.dev.java.net/.
Ах, выглядит интересно.Я также слышал о таких инструментах, как SOAPUI и ему подобных, которые можно использовать для функционального тестирования.Кто-нибудь здесь добился успеха с такими инструментами?
Спасибо за все ответы и извините за двусмысленное объяснение;Английский не мой родной язык.
-Саске