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

Давайте посмотрим на это:

    во-первых, новая абстракция среды, представленная в Здесь используется 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();
    }
}

Обратите внимание, что эти маршаллеры сами по себе не являются bean-компонентами 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 и Jenkins

Если 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.