文章导读
一、了解创建集成测试中的问题
作为开发人员尝试创建集成测试时,会遇到许多复杂问题。出现的两个最常见的问题包括与:
外部系统:要测试使用外部服务(如数据库,消息代理或遗留系统)的代码,需要运行这些外部系统。否则,无法正确评估该代码的功能。
未实现的服务:在开发期间,某些服务可能无法使用,因为项目中存在意外延迟。
在这两种情况下,开发人员都无法使用依赖服务来运行测试。要解决这些缺少的依赖项,开发人员必须构建可以模仿缺席服务的工具,例如轻量级消息代理,内存数据库或虚拟遗留系统。
或者,开发人员可以使用模拟框架。模拟框架提供了拦截对Java接口或类进行调用并返回测试可以使用的虚拟值的机制。
与dummy服务不同,模拟框架方法不要求在外部启动这些服务或在Java代码中实例化它们以触发测试。这意味着它不会消耗这些外部服务所需的相同内存和CPU周期,从而节省了时间和资源。
在初始开发周期中,使用模拟框架可以避免开发延迟,并支持良好的开发实践,包括使用接口来定义与外部服务的通信协议。 但是,重要的是要记住,模拟不能直接替代真正的集成测试。
二、使用模拟框架和其他微服务测试工具进行开发
在Java项目中有许多模拟框架选项。 在微服务驱动的开发中,使用支持微服务调用方式的框架非常重要,例如基于REST和Java API调用。 有一些简化测试开发的模拟框架,例如:
Wiremock:一个REST模拟工具,模仿对其他微服务的调用。 它消除了在测试之前启动外部服务的需要。
Mockito:用于代理Java接口方法调用的模拟框架。 Mockito还可用于验证方法调用顺序并提供测试应用程序所需的返回值。
这两个库都提供了大量功能,可以简化开发人员创建测试所需的工作,并降低与外部系统的集成点。
开发微服务测试时的另一个常见问题是每个单元测试通常会检查许多相同的条件,例如REST方法调用的返回值,或现有对象的最终状态。 这意味着开发人员需要编写大量样板代码来建立HTTP连接并比较预期值和测试结果。 有许多工具可以帮助缓解这些问题。 本文涵盖两个最常见的内容:
三、Wiremock
Wiremock是一个REST模拟框架,它模拟对其他REST API的调用。 它用于测试已经使用Arquillian部署的微服务中对外部服务进行的调用的处理。 Wiremock允许开发人员控制REST端点提供的响应。
要使用Wiremock,项目中的pom.xml文件必须通过添加以下依赖项来引用它:
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-standalone</artifactId>
<scope>test</scope>
</dependency>
要导入Wiremock使用的类和静态方法,请在测试类中添加以下导入声明:
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
要模拟对REST API的调用,请启动模拟服务器,该服务器将通过使用@Rule注释声明属性来响应对服务的请求:
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().port(7070));
在前面的示例中,服务器侦听端口7070上的请求。
为了模仿REST服务的响应,在执行测试之前声明了REST端点,HTTP方法和预期响应:
wireMockRule.stubFor(get(urlMatching("/api/aloha"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("Aloha [MOCK]")));
在前面的代码中,对/ api / aloha REST端点的任何请求都返回一个HTTP代码200,其头部定义了内容类型(application / json)和body负载(Aloha [MOCK])。
四、Mockito
Mockito是一个专注于Java代码测试的模拟框架。 它具有大多数模拟框架无法提供的重要功能,例如:
要在项目中使用Mockito,请使用pom.xml文件导入依赖项:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
要启用创建基于Mockito的测试所需的所有静态方法,请在测试类中声明以下导入:
import static org.mockito.Mockito.*;
要模拟类或接口,请在创建测试执行之前在测试方法中包含以下调用:
ClassOrInterface mock = mock(ClassOrInterface.class);
验证方法验证对模拟对象进行的方法调用。 在以下示例中,开发人员期望调用mock中的方法。
List list = mock(List.class);
verify(list.get(anyInt()));
要在调用方法时返回值,请使用when静态方法。 在以下示例中,对get方法的调用返回一个空的List值:
List list = mock(List.class);
when(list.get(anyInt()).thenReturn(Collections<Object>.emptyList());
五、Rest Assured
要评估REST API的输出,开发人员通常必须手动处理JSON数据。 Rest Assured提供了一个界面,可以最大限度地减少使用复杂API解析JSON数据的需要。
要在项目中使用Rest Assured,请使用pom.xml文件导入依赖项:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
要使用Rest Assured静态方法,请在测试类中添加以下import声明:
import static io.restassured.RestAssured.*;
每种测试方法都必须使用给定的方法来触发Rest Assured启动。 when方法定义了触发REST API所需的一些初始信息,例如端点和一些参数以及标头值。
then方法标识REST调用输出中的期望值。
given()
.when()
.get("/api/hola-chaining")
.then()
.statusCode(200);
、
对于复杂结果,评估可以使用JSONPath表示法来检查正文输出:
given()
.get("/api/hola")
.then()
.body("user.login", equalTo("john doe"));
要将主体的输出存储到变量,Rest Assured提供了提取方法。 该方法处理来自正文的输出,并使用as方法将其存储在变量中。 在以下示例中,extract方法将来自REST端点调用执行的数据存储在body变量中。
String body=given()
.get("/api/hola")
.then()
.extract().as(String.class);
六、Hamcrest
Hamcrest是一组静态方法,用于简化测试结果的评估。 根据传统的测试框架,测试通过创建一些断言来验证方法执行中的数据:
assertEquals(1,calc.result());
对于复杂的评估,该方法可能会变得复杂:
assertEquals("1",calc.getMemory().get(1).toString());
Hamcrest使测试代码可读,因为它定义了一个模仿英语的流畅界面:
assertThat("1", is(equalTo(calc.getMemory().get(1).toString())));
要在项目中使用Hamcrest,请使用pom.xml文件导入依赖项:
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
要启用创建基于Hamcrest的测试所需的所有类和静态方法,请在测试类中声明以下导入:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
七、实验展现
首先通过JBDS导入一个已经存在的maven项目。
通过在JBDS左侧窗格的Project Explorer选项卡中展开微服务扬声器项打开MockResourceSpeakerTest测试用例,然后单击microservice-speaker→Java Resources→src / test / java→io.microprofile.showcase.speaker.rest并展开 它。 双击MockResourceSpeakerTest.java文件。
源代码主要由提供方向的注释组成。 testGet测试方法必须检查REST端点/扬声器是否返回一组已注册到会议应用程序的扬声器。 但是,该测试目前尚未实现,并且正在从JUnit调用fail方法。
检查模拟服务器实例化。 为了接受REST端点调用,测试具有WireMockRule属性。 它实例化响应请求的模拟服务器。 要将模拟服务器配置为在端口7070上运行,请使用options()。port(7070)方法。 JUnit使用@Rule注释在所有测试方法上启动和停止模拟服务器。
配置Wiremock服务器。 测试方法向微服务会话应用程序发送REST调用,但是没有为此测试目的启动微服务。 要回答请求,必须由开发人员配置模拟服务器。 为此,请使用WireMockRule属性为模拟服务器准备调用。
准备模拟服务器以将请求的答案发送到/ sessions / speaker / speakerId / 99 URI。 底层微服务返回一个会话ID列表,其发言人ID为99。
要分析微服务 - 扬声器应用程序调用的REST端点,请通过在JBDS左窗格中的Project Explorer选项卡中展开microservice-session项打开SessionResource类,然后单击microservice-session→Java Resources→src / main / java →io.microprofile.showcase.session并展开它。 双击SessionResource.java文件并查找getSpeakersSession方法。
使用REST Assured实施测试。 要调用REST端点,请使用REST Assured API。
运行JUnit测试用例。
右键单击MockResourceSpeakerTest测试用例,然后在JBDS中选择Run As→JUnit Test。 JUnit选项卡显示测试用例执行的输出。 这次,测试通过并在测试执行后显示绿色条。