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

文章导读

  • 本文仅代表作者的个人观点;
  • 本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
  • 本文素材是红帽公司产品技术和手册;
  • 本文分为系列文章,将会有多篇,初步预计将会有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文件必须通过添加以下依赖项来引用它:

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

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

要在项目中使用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方法。

  • 在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选项卡显示测试用例执行的输出。 这次,测试通过并在测试执行后显示绿色条。

原文发布于微信公众号 - 大魏分享(david-share)

原文发表时间:2018-08-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏吴伟祥

Settings -> Plugins 原

Free Mybatis Plugins    (*mapper.java-- *mapper.xml)

792
来自专栏Gaussic

使用 Spring HATEOAS 开发 REST 服务

原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-SpringHATEOAS/

1152
来自专栏代码GG之家

封装之路(一) BaseApp

架构基于 Dagger2 + RxJava + Retrofit + Material Design + MVVM 目标:简化开发app难度,轻松容易的实...

2159
来自专栏沃趣科技

MySQL中server_id一致带来的问题

简 介 我们都知道在MySQL搭建复制环境的时候,需要设置每个server的server_id不一致,如果主库与从库的server_id一致,那么复制会失败。...

4236
来自专栏JAVA高级架构

Java面试分享(题目+答案)

3233
来自专栏程序员叨叨叨

【转】使用 Spring HATEOAS 开发 REST 服务原文

绝大多数开发人员对于 REST 这个词都并不陌生。自从 2000 年 Roy Fielding 在其博士论文中创造出来这个词之后,REST 架构风格就很快地流行...

1291
来自专栏微服务生态

玩转EhCache之最简单的缓存框架

Ehcache是一个用Java实现的使用简单,高速,实现线程安全的缓存管理类库,ehcache提供了用内存,磁盘文件存储,以及分布式存储方式等多种灵活的cach...

7014
来自专栏技术墨客

Hazelcast集群服务(1)——Hazelcast介绍

    “分布式”、“集群服务”、“网格式内存数据”、“分布式缓存“、“弹性可伸缩服务”——这些牛逼闪闪的名词拿到哪都是ITer装逼的不二之选。在Javaer的...

3653
来自专栏程序员宝库

给你一份详细的 Spring Boot 知识清单

在过去两三年的Spring生态圈,最让人兴奋的莫过于Spring Boot框架。或许从命名上就能看出这个框架的设计初衷:快速的启动Spring应用。因而Spri...

1195
来自专栏阿杜的世界

聊聊单元测试

单元测试(unit testing):是指对软件中的最小可测试单元进行检查和验证。

1241

扫码关注云+社区

领取腾讯云代金券