前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何使用模拟框架测试微服务? | 微服务系列第八篇

如何使用模拟框架测试微服务? | 微服务系列第八篇

作者头像
魏新宇
发布2018-09-30 11:43:44
3.5K0
发布2018-09-30 11:43:44
举报

文章导读

  • 本文仅代表作者的个人观点;
  • 本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
  • 本文素材是红帽公司产品技术和手册;
  • 本文分为系列文章,将会有多篇,初步预计将会有26篇。

一、了解创建集成测试中的问题

作为开发人员尝试创建集成测试时,会遇到许多复杂问题。出现的两个最常见的问题包括与:

  1. 不可靠或不可用的外部系统集成
  2. 与尚未实现的服务集成。

外部系统:要测试使用外部服务(如数据库,消息代理或遗留系统)的代码,需要运行这些外部系统。否则,无法正确评估该代码的功能。

未实现的服务:在开发期间,某些服务可能无法使用,因为项目中存在意外延迟。

在这两种情况下,开发人员都无法使用依赖服务来运行测试。要解决这些缺少的依赖项,开发人员必须构建可以模仿缺席服务的工具,例如轻量级消息代理,内存数据库或虚拟遗留系统。

或者,开发人员可以使用模拟框架。模拟框架提供了拦截对Java接口或类进行调用并返回测试可以使用的虚拟值的机制。

与dummy服务不同,模拟框架方法不要求在外部启动这些服务或在Java代码中实例化它们以触发测试。这意味着它不会消耗这些外部服务所需的相同内存和CPU周期,从而节省了时间和资源。

在初始开发周期中,使用模拟框架可以避免开发延迟,并支持良好的开发实践,包括使用接口来定义与外部服务的通信协议。 但是,重要的是要记住,模拟不能直接替代真正的集成测试。

二、使用模拟框架和其他微服务测试工具进行开发

在Java项目中有许多模拟框架选项。 在微服务驱动的开发中,使用支持微服务调用方式的框架非常重要,例如基于REST和Java API调用。 有一些简化测试开发的模拟框架,例如:

Wiremock:一个REST模拟工具,模仿对其他微服务的调用。 它消除了在测试之前启动外部服务的需要。

Mockito:用于代理Java接口方法调用的模拟框架。 Mockito还可用于验证方法调用顺序并提供测试应用程序所需的返回值。

这两个库都提供了大量功能,可以简化开发人员创建测试所需的工作,并降低与外部系统的集成点。

开发微服务测试时的另一个常见问题是每个单元测试通常会检查许多相同的条件,例如REST方法调用的返回值,或现有对象的最终状态。 这意味着开发人员需要编写大量样板代码来建立HTTP连接并比较预期值和测试结果。 有许多工具可以帮助缓解这些问题。 本文涵盖两个最常见的内容:

  • Rest Assured使用流畅的接口调用REST API,它简化了使用任何测试框架(如JUnit或TestNG)在测试中进行REST调用的方式。
  • Hamcrest提供静态方法,使用流畅的接口使源代码更易读和可维护。

三、Wiremock

Wiremock是一个REST模拟框架,它模拟对其他REST API的调用。 它用于测试已经使用Arquillian部署的微服务中对外部服务进行的调用的处理。 Wiremock允许开发人员控制REST端点提供的响应。

要使用Wiremock,项目中的pom.xml文件必须通过添加以下依赖项来引用它:

代码语言:javascript
复制
<dependency>
	<groupId>com.github.tomakehurst</groupId>
	<artifactId>wiremock-standalone</artifactId>
	<scope>test</scope>
</dependency>

要导入Wiremock使用的类和静态方法,请在测试类中添加以下导入声明:

代码语言:javascript
复制
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注释声明属性来响应对服务的请求:

代码语言:javascript
复制
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().port(7070));

在前面的示例中,服务器侦听端口7070上的请求。

为了模仿REST服务的响应,在执行测试之前声明了REST端点,HTTP方法和预期响应:

代码语言:javascript
复制
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代码测试的模拟框架。 它具有大多数模拟框架无法提供的重要功能,例如:

  • 模拟抽象和具体类:在定义应该开发的协议以便将系统与Java接口集成时,模拟框架很有用。 但是,有时候,某些代码可能已经开发为抽象或具体类。 如果您需要模拟现有的Java类或接口,Mockito可以模拟具体或抽象类。
  • 检查对方法的调用次数:某些模拟框架仅评估模拟类或接口中的方法是否按特定顺序调用。 Mockito不仅可以评估方法是否被调用,还可以计算调用次数及其顺序。 如果需要严格评估,Mockito可以强制执行订单和呼叫次数。

要在项目中使用Mockito,请使用pom.xml文件导入依赖项:

代码语言:javascript
复制
<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-core</artifactId>
	<scope>test</scope>
</dependency>

要启用创建基于Mockito的测试所需的所有静态方法,请在测试类中声明以下导入:

代码语言:javascript
复制
import static org.mockito.Mockito.*;

要模拟类或接口,请在创建测试执行之前在测试方法中包含以下调用:

代码语言:javascript
复制
ClassOrInterface mock = mock(ClassOrInterface.class); 

验证方法验证对模拟对象进行的方法调用。 在以下示例中,开发人员期望调用mock中的方法。

代码语言:javascript
复制
List list = mock(List.class);
verify(list.get(anyInt()));

要在调用方法时返回值,请使用when静态方法。 在以下示例中,对get方法的调用返回一个空的List值:

代码语言:javascript
复制
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文件导入依赖项:

代码语言:javascript
复制
<dependency>
	<groupId>io.rest-assured</groupId>
	<artifactId>rest-assured</artifactId>
	<scope>test</scope>
</dependency>

要使用Rest Assured静态方法,请在测试类中添加以下import声明:

代码语言:javascript
复制
import static io.restassured.RestAssured.*;

每种测试方法都必须使用给定的方法来触发Rest Assured启动。 when方法定义了触发REST API所需的一些初始信息,例如端点和一些参数以及标头值。

then方法标识REST调用输出中的期望值。

代码语言:javascript
复制
  given()
    .when()
      .get("/api/hola-chaining")
    .then()
      .statusCode(200);

对于复杂结果,评估可以使用JSONPath表示法来检查正文输出:

代码语言:javascript
复制
given()
  .get("/api/hola")
.then()
  .body("user.login", equalTo("john doe"));

要将主体的输出存储到变量,Rest Assured提供了提取方法。 该方法处理来自正文的输出,并使用as方法将其存储在变量中。 在以下示例中,extract方法将来自REST端点调用执行的数据存储在body变量中。

代码语言:javascript
复制
String body=given()
  .get("/api/hola")
.then()
  .extract().as(String.class);

六、Hamcrest

Hamcrest是一组静态方法,用于简化测试结果的评估。 根据传统的测试框架,测试通过创建一些断言来验证方法执行中的数据:

代码语言:javascript
复制
assertEquals(1,calc.result());

对于复杂的评估,该方法可能会变得复杂:

代码语言:javascript
复制
assertEquals("1",calc.getMemory().get(1).toString());

Hamcrest使测试代码可读,因为它定义了一个模仿英语的流畅界面:

代码语言:javascript
复制
assertThat("1", is(equalTo(calc.getMemory().get(1).toString())));

要在项目中使用Hamcrest,请使用pom.xml文件导入依赖项:

代码语言:javascript
复制
<dependency>
	<groupId>org.hamcrest</groupId>
	<artifactId>hamcrest-library</artifactId>
	<scope>test</scope>
</dependency>

要启用创建基于Hamcrest的测试所需的所有类和静态方法,请在测试类中声明以下导入:

代码语言:javascript
复制
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方法。

  • 在testGet方法的开头,从wireMockRule类属性中调用stubFor方法。 要回答HTTP GET方法调用,请调用get static方法。 提供urlMatching(“/ sessions / speaker / speakerId / 99”)方法调用的结果作为参数。
  • 要响应REST端点调用,请调用willReturn()方法。
  • 希望响应是HTTP代码200.使用aResponse()。withStatus(200)静态方法创建此响应并将其传递给willReturn()方法。
  • 模拟以speaker作为有效载荷返回JSON数据。 要准备客户端以接收JSON数据,必须声明Content-Type HTTP标头。
  • JSON数据由名为sessions的预先存在的属性提供。 使用此属性将数据传递到withBody()方法,以便将此数据作为HTTP正文内容发送。

使用REST Assured实施测试。 要调用REST端点,请使用REST Assured API。

  • 调用给定方法以启动REST Assured客户端。 在Wiremock服务器准备之后,调用REST Assured given 方法。
  • 调用when方法以准备REST Assured以调用REST端点。
  • 使用“/ speaker / sessions / speakerId / 99”参数调用get静态方法以调用HTTP GET方法。
  • 通过调用then方法检查预期的输出。
  • 预期输出是具有三个会话ID的JSON数组。 要验证这一点,请使用REST Assured断言机制中的size()函数。

运行JUnit测试用例。

右键单击MockResourceSpeakerTest测试用例,然后在JBDS中选择Run As→JUnit Test。 JUnit选项卡显示测试用例执行的输出。 这次,测试通过并在测试执行后显示绿色条。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大魏分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档