Testing REST with multiple MIME types – 用多种MIME类型测试REST

最后修改: 2013年 1月 18日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This article will focus on testing a REST Service with multiple Media Types/representations.

本文将专注于测试具有多种媒体类型/代表的REST服务

We will write integration tests capable of switching between the multiple types of Representations supported by the API. The goal is to be able to run the exact same test consuming the exact same URIs of the service, just asking for a different Media Type.

我们将编写集成测试,能够在API支持的多种类型的表现形式之间切换。我们的目标是能够运行完全相同的测试,消耗完全相同的服务URI,只是要求不同的媒体类型。

2. Goals

2.目标

Any REST API needs to expose its Resources as representations using one or more Media Types. The client will set the Accept header to choose the type of representation it asks for from the service.

任何REST API都需要使用一种或多种媒体类型将其资源作为表示方法公开。客户端将设置Accept头,以选择它向服务请求的表示类型。

Since the Resource can have multiple representations, the server will have to implement a mechanism responsible for choosing the right representation. This is also known as Content Negotiation.

由于资源可以有多种表现形式,服务器将必须实现一种机制,负责选择正确的表现形式。这也被称为 “内容协商”。

Thus, if the client asks for application/xml, then it should get an XML representation of the Resource. And if it asks for application/json, then it should get JSON.

因此,如果客户端请求application/xml,那么它应该得到资源的XML表示。而如果它要求application/json,那么它应该得到JSON。

3. Testing Infrastructure

3.测试基础设施

We’ll begin by defining a simple interface for a marshaller. This will be the main abstraction that will allow the test to switch between different Media Types:

我们将首先定义一个marshaller的简单接口。这将是主要的抽象,允许测试在不同的媒体类型之间切换。

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

Then we need a way to initialize the right marshaller based on some form of external configuration.

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

For this, we’ll use a Spring FactoryBean to initialize the marshaller and a simple property to determine which marshaller to use:

为此,我们将使用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;
    }
}

Let’s look at this:

让我们来看看这个。

  • first, the new Environment abstraction introduced in Spring 3.1 is used here – for more on this check out the detailed article on using Properties with Spring
  • we retrieve the test.mime property from the environment and use it to determine which marshaller to create – some Java 7 switch on String syntax at work here
  • next, the default marshaller, in case the property isn’t defined at all, is going to be the Jackson marshaller for JSON support
  • finally – this BeanFactory is only active in a test scenario, as we’re using the @Profile support, also introduced in Spring 3.1

That’s it – the mechanism is able to switch between marshallers based on whatever the value of the test.mime property is.

就是这样–该机制能够根据test.mime属性的任何值在marshallers之间进行切换。

4. The JSON and XML Marshallers

4.JSON和XML马歇尔

Moving on, we’ll need the actual marshaller implementation – one for each supported Media Type.

接着,我们将需要实际的marshaller实现–每个支持的媒体类型都有一个。

For JSON we’ll use Jackson as the underlying library:

对于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();
    }
}

For the XML support, the marshaller uses XStream:

对于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();
    }
}

Note that these marshallers are not Spring beans themselves. The reason for that is they will be bootstrapped into the Spring context by the TestMarshallerFactory; there’s no need to make them components directly.

请注意,这些marshaller不是Spring Bean本身。原因是它们将被TestMarshallerFactory引导到Spring上下文中;没有必要让它们直接成为组件。

5. Consuming the Service With Both JSON and XML

5.用JSON和XML来消费服务

At this point, we should be able to run a full integration test against the deployed service. Using the marshaller is straightforward: we’ll inject an IMarshaller into the test:

在这一点上,我们应该能够针对部署的服务运行一个完整的集成测试。使用marshaller很简单:我们将注入一个 IMarshaller到测试中。

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

    @Autowired
    private IMarshaller marshaller;

    // tests
    ...

}

Spring will decide the exact marshaller to inject based on the value of the test.mime property.

Spring将根据test.mime属性的值决定要注入的确切的marshaller。

If we don’t provide a value for this property, the TestMarshallerFactory will simply fall back on the default marshaller – the JSON marshaller.

如果我们不为这个属性提供一个值,TestMarshallerFactory将简单地返回到默认的marshaller – JSON marshaller。

6. Maven and Jenkins

6.Maven和Jenkins

If Maven is set up to run integration tests against an already deployed REST Service, then we can run it using:

如果Maven被设置为针对已经部署的REST服务运行集成测试,那么我们就可以用。

mvn test -Dtest.mime=xml

Or, if this the build uses the integration-test phase of the Maven lifecycle:

或者,如果这时的构建使用Maven生命周期中的集成-测试阶段。

mvn integration-test -Dtest.mime=xml

For more details on how to set up the Maven build to run integration tests, see the Integration Testing with Maven article.

关于如何设置Maven构建以运行集成测试的更多细节,请参阅用Maven进行集成测试文章。

With Jenkins, we must configure the job with:

使用Jenkins,我们必须用以下方式配置工作。

This build is parametrized

And the String parameter: test.mime=xml added.

以及String参数test.mime=xml添加。

A common Jenkins configuration would be having to jobs running the same set of integration tests against the deployed service – one with XML and the other with JSON representations.

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

7. Conclusion

7.结论

This article showed how to test a REST API that works with multiple representations. Most APIs do publish their Resources under multiple Representations, so testing all of these is vital. The fact that we can use the exact same tests across all of them is just cool.

这篇文章展示了如何测试一个使用多种表现形式的REST API。大多数API确实在多个表示法下发布他们的资源,所以测试所有这些表示法是至关重要的。事实上,我们可以在它们之间使用完全相同的测试,这很酷。

The full implementation of this mechanism – using actual integration tests and verifying both the XML and JSON representations – can be found in the GitHub project.

该机制的完整实现–使用实际的集成测试并验证 XML 和 JSON 表示法–可以在GitHub 项目中找到。