1. 概述

本文将专注于测试支持多种媒体类型/表示的REST服务。

我们将编写能够切换到API支持的不同类型表示的集成测试。目标是在请求服务的相同URI时,只需更改媒体类型,就能运行完全相同的测试。

2. 目标

任何REST API都需要使用一种或多种媒体类型来展示其资源。客户端会在请求中设置Accept头,以从服务中选择它所要求的表示形式。

由于资源可以有多种表示形式,服务器需要实现一个负责选择正确表示的机制。这通常被称为内容协商。

因此,如果客户端请求application/xml,则应获取资源的XML表示。如果请求application/json,则应获取JSON表示。

3. 测试基础设施

首先,我们定义一个简单的 Marshaller 接口。这个接口将是测试切换不同媒体类型的主抽象:

public interface IMarshaller {
    ...
    String getMime();
}

然后我们需要一种方法根据某种外部配置初始化正确的 Marshaller。

为此,我们将使用Spring的FactoryBean来初始化 Marshaller,并使用一个简单的属性来确定使用哪个 Marshaller:

@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中引入的新Environment抽象——有关如何使用Properties与Spring的详细信息,请参阅在Spring 3.1中使用Properties
  • 我们从环境环境中获取test.mime属性,并使用它来确定创建哪个 Marshaller——这里使用了Java 7的字符串开关语法。
  • 如果该属性未定义,那么默认 Marshaller 将是用于JSON支持的Jackson Marshaller。
  • 最后,这个BeanFactory只在测试场景中活动,因为我们使用了Spring 3.1中引入的@Profile支持。

就这样,机制可以根据test.mime属性的值切换 Marshaller。

4. JSON和XML Marshaller

接下来,我们需要实际的 Marshaller 实现,每个支持的媒体类型一个。

对于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支持,Marshaller使用XStream

public class XStreamMarshaller implements IMarshaller {
    private XStream xstream;

    public XStreamMarshaller() {
        super();
        xstream = new XStream();
    }

    ...

    public String getMime() {
        return MediaType.APPLICATION_XML.toString();
    }
}

请注意,这些Marshaller本身并不是Spring bean。原因是它们将由TestMarshallerFactory注入到Spring上下文中,无需直接使其成为组件。

5. 使用JSON和XML消费服务

此时,我们应该能够对部署的服务运行完整的集成测试。使用Marshaller非常简单:我们将IMarshaller注入到测试中:

@ActiveProfiles({ "test" })
public abstract class SomeRestLiveTest {

    @Autowired
    private IMarshaller marshaller;

    // tests
    ...

}

Spring会根据test.mime属性的值决定注入哪个确切的Marshaller。

如果我们不提供这个属性的值,TestMarshallerFactory将简单地回退到默认的Marshaller——JSON Marshaller。

6. Maven和Jenkins

如果Maven设置为针对已部署的REST服务运行集成测试,那么我们可以使用以下命令运行:

mvn test -Dtest.mime=xml

或者,如果构建使用Maven生命周期的integration-test阶段:

mvn integration-test -Dtest.mime=xml

有关如何设置Maven构建以运行集成测试的更多详细信息,请参阅使用Maven Cargo插件进行集成测试文章。

在Jenkins中,我们必须配置工作流,如下所示:

This build is parametrized

并添加一个String参数test.mime=xml

常见的Jenkins配置是运行针对部署服务的同一套集成测试——一个使用XML表示,另一个使用JSON表示。

7. 总结

本文展示了如何测试一个支持多种表示形式的REST API。大多数API确实会在多个表示下发布其资源,因此测试所有这些表示至关重要。而且,我们可以在所有这些表示上使用相同的测试,这一点很酷。

这个机制的实际实现——使用实际的集成测试并验证XML和JSON表示——可以在GitHub项目中找到。