1. Обзор
В этой статье основное внимание будет уделено тестированию службы REST с несколькими типами/представлениями мультимедиа.
Мы напишем интеграционные тесты, способные переключаться между несколькими типами представлений, поддерживаемыми API. Цель состоит в том, чтобы иметь возможность запускать один и тот же тест, используя те же самые URI службы, просто запрашивая другой тип носителя.
2. Цели
Любой REST API должен предоставлять свои ресурсы как представления с использованием одного или нескольких типов мультимедиа. Клиент установит заголовок Accept
, чтобы выбрать тип представления, который он запрашивает у службы.
Так как Ресурс может иметь несколько представлений, сервер должен будет реализовать механизм, отвечающий за выбор правильного представления. Это также известно как согласование содержания.
Таким образом, если клиент запрашивает application/xml
, он должен получить XML-представление Ресурса. И если он запрашивает application/json
, то он должен получить JSON.
3. Инфраструктура тестирования
Мы начнем с определения простого интерфейса для маршаллера. Это будет основная абстракция, которая позволит тесту переключаться между различными типами медиа:
public interface IMarshaller {
...
String getMime();
}
Затем нам нужен способ инициализировать правильный маршаллер на основе некоторой формы внешней конфигурации.
Для этого мы будем использовать Spring FactoryBean
для инициализации маршаллера и простое свойство, чтобы определить, какой маршаллер использовать :
@Component
@Profile("test")
public class TestMarshallerFactory implements FactoryBean<IMarshaller> {
@Autowired
private Environment env;
public IMarshaller getObject() {
String testMime = env.getProperty("test.mime");
if (testMime != null) {
switch (testMime) {
case "json":
return new JacksonMarshaller();
case "xml":
return new XStreamMarshaller();
default:
throw new IllegalStateException();
}
}
return new JacksonMarshaller();
}
public Class<IMarshaller> getObjectType() {
return IMarshaller.class;
}
public boolean isSingleton() {
return true;
}
}
Давайте посмотрим на это:
- во-первых, здесь используется новая абстракция
Environment
, представленная в Spring 3.1 — для получения дополнительной информации ознакомьтесь с подробной статьей об использовании свойств с Spring - мы извлекаем свойство
test.mime
из среды и используем его, чтобы определить, какой маршаллер создать — здесь некоторые переключатели Java 7 включают синтаксисString
- затем маршаллер по умолчанию, если свойство вообще не определено, будет маршалером Джексона для поддержки JSON.
- наконец — этот
BeanFactory
активен только в тестовом сценарии, так как мы используем поддержку@Profile
, также представленную в Spring 3.1 .
Вот и все — механизм может переключаться между маршаллерами в зависимости от значения свойства test.mime
.
4. Маршаллеры JSON и XML
Двигаясь дальше, нам понадобится фактическая реализация маршаллера — по одной для каждого поддерживаемого типа носителя.
Для JSON мы будем использовать Jackson
в качестве базовой библиотеки:
public class JacksonMarshaller implements IMarshaller {
private ObjectMapper objectMapper;
public JacksonMarshaller() {
super();
objectMapper = new ObjectMapper();
}
...
@Override
public String getMime() {
return MediaType.APPLICATION_JSON.toString();
}
}
Для поддержки XML маршаллер использует XStream
:
public class XStreamMarshaller implements IMarshaller {
private XStream xstream;
public XStreamMarshaller() {
super();
xstream = new XStream();
}
...
public String getMime() {
return MediaType.APPLICATION_XML.toString();
}
}
Обратите внимание, что эти маршаллеры сами по себе не являются компонентами Spring . Причина этого в том, что они будут загружены в контекст Spring с помощью TestMarshallerFactory;
нет необходимости делать их компонентами напрямую.
5. Использование службы как с JSON, так и с XML
На этом этапе мы должны иметь возможность запустить полный интеграционный тест для развернутой службы. Использовать маршаллер очень просто: мы внедрим IMarshaller
в тест:
@ActiveProfiles({ "test" })
public abstract class SomeRestLiveTest {
@Autowired
private IMarshaller marshaller;
// tests
...
}
Spring решит, какой маршаллер ввести, основываясь на значении свойства test.mime
.
Если мы не укажем значение для этого свойства, TestMarshallerFactory
просто вернется к маршаллеру по умолчанию — маршаллеру JSON.
6. Мейвен и Дженкинс
Если Maven настроен для запуска интеграционных тестов с уже развернутой службой REST, мы можем запустить ее, используя:
mvn test -Dtest.mime=xml
Или, если эта сборка использует этап интеграционного тестирования
жизненного цикла Maven:
mvn integration-test -Dtest.mime=xml
Дополнительные сведения о том, как настроить сборку Maven для запуска интеграционных тестов, см. в статье Интеграционное тестирование с Maven
.
С Jenkins мы должны настроить задание с помощью:
This build is parametrized
И добавлен параметр String
: test.mime=xml
. **
**
Обычная конфигурация Jenkins будет заключаться в том, чтобы задания выполняли один и тот же набор интеграционных тестов для развернутой службы — одно с XML, а другое с представлениями JSON.
7. Заключение
В этой статье показано, как тестировать REST API, который работает с несколькими представлениями. Большинство API-интерфейсов публикуют свои ресурсы в нескольких представлениях, поэтому тестирование всех них жизненно важно. Тот факт, что мы можем использовать одни и те же тесты для всех из них, просто крут.
Полную реализацию этого механизма — с использованием актуальных интеграционных тестов и проверкой как XML, так и JSON-представлений — можно найти в проекте GitHub .