作者: Eugen Paraschiv
译者: helloworldtang
目录
- 1. 概览
- 2. 目标
- 3. 测试基础设施
- 4. JSON和XML Marshaller
- 5. 使用JSON和XML来消费服务
- 6. Maven和Jenkins
- 7. 总结
名词释义:
- 资源表述:在REST中的一切都被认为是一种资源,XML或JSON分别是两种不同的表述形式
- Marshaller【在业界没有一个统一的中文词汇】:是字符串和对象进行互相转换的组件的统称,譬如,java中的XML Marshaller,可以将Java对象转换成xml,也可以将xml字符串转换为Java对象。
1. 概览
本文将着重于测试具有多种媒体类型/资源表述的REST服务。
我们将编写能够在API支持的多种资源表述之间切换的集成测试。具体目标是,即使更改了请求的媒体类型 ,也能够运行完全相同的测试,并且这个测试所消费服务的URI也不需要更改。
2. 目标
任何REST API都需要以某种媒体类型的表述来暴露其资源,在许多情况下,它不止一个。客户端通过设置Accept头来选择它从服务请求的资源表述类型。
因为资源可以有多种表述,所以服务器必须实现一个负责选择正确表述的机制——也称为内容协商。因此,如果客户端请求application/xml,那么它应该得到XML表述的资源,如果它请求application/json,那么它应该得到JSON。
3. 测试基础设施
首先,我们将为Marshaller定义一个简单的接口——这将是允许测试用例在不同媒体类型之间切换的主要抽象:
代码语言:javascript复制public interface IMarshaller {
...
String getMime();
}
然后,我们需要一种方法来根据某种形式的外部配置初始化正确的Marshaller。对于这种机制,我们将使用Spring FactoryBean来初始化Marshaller和一个简单的属性,以确定要使用哪个Marshaller:
代码语言:javascript复制@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——更多的介绍可以看这里:关于使用Spring Properties的详细文章。
- test.mime属性是从Environment中检索出来的,用于确定要创建哪个Marshaller——在这里使用了Java 7 switch支持String的语法。
- 接下来,设置默认Marshaller为Jackson Marshaller。如果没有定义属性,资源表述的媒体类型将会是JSON
- 最后,这个BeanFactory只在这个测试场景中生效,因为使用了Spring 3.1中引入了的新特性@Profile。
就是这样——这个机制能够根据test.mime属性值来切换Marshaller。
4. JSON和XML Marshaller
继续,我们将需要实际的Marshaller实现——针对每种受支持的媒体类型。
对于JSON,我们将使用Jackson作为底层库:
代码语言:javascript复制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:
代码语言:javascript复制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直接注入到测试用例中:
代码语言:javascript复制@ActiveProfiles({ "test" })
public abstract class SomeRestLiveTest {
@Autowired
private IMarshaller marshaller;
// tests
...
}
在Spring中注入精确的Marshaller当然是由test.mime属性值决定的;这可以从属性文件中获取,也可以手动配置在测试环境中。如果这个属性没有提供一个值,那么TestMarshallerFactory就会简单地回到默认的Marshaller——JSON marshaller。
6. Maven和Jenkins
如果Maven被配置为针对已经部署的REST服务运行集成测试,那么它可以像这样运行:
代码语言:javascript复制mvn test -Dtest.mime=xml
或者,如果这个构建使用Maven生命周期的integration-test阶段:
代码语言:javascript复制mvn integration-test -Dtest.mime=xml
有关如何使用这些阶段以及如何配置Maven构建的更多细节,以便将应用程序部署绑定到pre-integration-test目标,在集成测试目标中运行集成测试,然后在post-integration-test中关闭已部署的服务,参见 使用Maven进行集成测试
对于Jenkins来说,Maven任务必须配置为:
代码语言:javascript复制This build is parametrized
并且,需要新增这个字符串参数: test.mime=xml.
一个常见的Jenkins配置将不得不使用与已部署的服务运行相同的集成测试集——一个带有XML,另一个带有JSON表述。
7. 总结
本文展示了如何测试一个具有多重表述的REST API。大多数API都在多个表述中发布它们的资源,因此测试所有这些都是至关重要的;事实上,我们可以在所有这些测试中使用完全相同的测试,这是很酷的。
所有这些示例和代码片段都可以在GitHub上找到。这是一个基于Eclipse的项目,所以它应该很容易导入和运行。