没经过这些测试,你的微服务架构也敢进入生产环境?

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 微服务架构是指将应用程序拆分为一系列较小、且直接用于解决具体问题的组件的实践方案。以此为基础,架构中的每一个组件都将通过各类常规协议(例如 HTTP 或者更轻量化的 TCP)相互通信。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

微服务架构是指将应用程序拆分为一系列较小、且直接用于解决具体问题的组件的实践方案。以此为基础,架构中的每一个组件都将通过各类常规协议(例如 HTTP 或者更轻量化的 TCP)相互通信。

说到这里,大家可能会好奇,对于微服务架构来说,测试真的很重要吗?

答案当然是重要!测试的重要性是体现在多方面的,不过比较重要的是以下几点:

  • 节约金钱与时间
  • 更安全
  • 强化生产质量(减少 bug 与错误数量)
  • 提升客户满意度
  • 最重要的是,夜里能睡得更安稳

随时出 Bug、动不动就宕机的应用程序,没人会喜欢,而且往往这种应用程序的安全漏洞很多,如果黑客想从中窃取凭证或者抢劫资金,简直易如反掌。如果我们想要开发一款具备一定复杂性的应用程序,那么测试是一定需要的。

使用什么测试方法?

目前软件测试的种类比较多,大致可以分为功能测试和非功能测试两大类。其中功能测试类包括单元测试、集成测试、通烟测试、回归测试、健全测试、Beta/ 验收测试和端到端(e2e)测试,而非功能测试则包括了性能测试、负载测试、压力测试、安全测试、合规测试和可用性测试。

一般来说,应用程序的复杂度越高,需要使用的测试类型也就越多。不过,有几个测试是所有应用程序都不可或缺的:

  • 单元测试
  • 集成测试
  • E2E 测试、回归测试与安全测试相结合

整体流程应该是,先编写程序来检查应用中各个层面是否在按照预期设计运作,若应用已经上线,那么就需要进一步编写测试来检查代码更新是否会对原有功能造成破坏。如果是微服务架构,那么除了以上的基础测试之外,可能还需要编写专门的测试,例如负载测试,用于检查系统在正常与预期峰值负载条件下的运行状况。

少说话,多编码

接下来,我们一起探讨一下如何在微服务架构当中实现上述基础软件测试类型。微服务架构使用 TCP 协议实现组件间的通信,并利用 Nest Framework 以 Node.JS 编写而成。

很多人可能不太了解 NestJS,我们先简单介绍一下,官方 GitHub repo 是这样描述 Nest 的:

Nest 是一款框架,用于构建高效且可扩展的 Node.js 服务器端应用程序。它利用现代 JavaScript 的特性,由 TypeScript 构建而成(保留纯 JavaScript 兼容性),同时结合有 OOP(面向对象编程)、FP(函数编程)以及 FRP(函数响应式编程)的元素。从底层来看,Nest 不仅能够使用 Express,同时也兼容其他多种库,包括 Fastify,旨在轻松使用大量现有第三方插件。”

在示例中,我们将使用一个简单的模块 name:user 配合一个简单函数 createUser,在数据库内创建一个新用户。
该模块的文件夹结构如下所示:

A77899F0_94C2_48e1_A3B1_71F85DDFCDC4

我们设了一个监听 create_user 消息的控制器。在利用 ValidationPipe 进行验证之后,它会在服务之内调用一个具有相同名称的函数。

CD3D6DD4_43A9_4aa7_9E52_C537E1B58123

在服务之内,我们会对用户密码进行哈希处理。接下来,使用 TypeORM 将新用户保存在数据库内。

B1825237_921B_4d7b_83AA_714A50D28776

对这个模块,我们使用 TypeORM 作为链接至表 User 的 ORM;同时,利用名为 UtilsModule 的另一个模块实现某些辅助功能:

3806F555_EEFE_4dce_BA24_0C0BA90F53E7
83AFF8F1_A318_4d87_B49A_FFC3C12D2215

单元测试

所谓单元,是指应用程序当中的最小可测试部分,例如函数、类或者过程。单元测试则代表一种软件测试方法,旨在测试源代码中的各个单元,以确定其是否符合开发阶段的预期设计。

编写单元测试,是为了保证不同代码形式(函数、类等)的每个简单实现,均符合设计、要求并能够按预期运行。
单元测试的目标,在于隔离程序中的各个部分,并测试这些部分是否正常工作。
换言之,与当前测试单元无关的其他代码部分,则以模拟形式存在,仅作为运行环境使用。
在我们的示例中,需要测试的单元自然就是之前提到的 createUser 了。这意味着我们首先得把它跟其他组件隔离开来。因此,第一步就是模拟 user repository 类,这个类代表着数据库使用 TypeORM 时的链接。

如果分析服务中的 createUser 函数,就会发现它的作用只是对密码进行哈希处理,而后将 User 对象保存在数据库内。以此为基础,我们编写出以下测试套件:

3334A927_1D79_4134_B197_D7274F6C6061

首先,我们编写一个 beforeAll 函数来创建测试模块。接下来,使用模拟类替换原始 repository,该模拟类将仅返回需要保存在数据库中的对象。

在这个函数中,我们还得考虑这样一种极端要求:

使用特定属性(邮件、密码等)创建一个新用户对象,请确保密码经过哈希处理

这里,我们会模拟 save() 函数,因为它来自 TypeORM、不属于测试中的对象单元范畴,使用简单函数将其覆盖以返回我们传递的对象。
到这里,我们的工作就很简单了:检查在发送对象过程中使用的邮件属性与哈希密码是否正确。

集成测试

集成测试属于另一种软件测试方法,用于验证源代码单元内的组成功能是否正常。

单元测试的目标是保证代码符合其设计与功能要求,同时能够按照预期方式运行。集成测试则更进一步,将不同模块融合在一起,并测试它们是否能够正确交互。

在本示例中,我们将 UserModule 与 TypeORM 模块(存在依赖关系)结合起来,检查新用户是否被正确保存在数据库内。

这一次,我们仍然需要使用之前提到的函数,只是具体测试流程有所区别:

55dc92a8ade4a7373bb3c40c0830dcff

这一次,beforeAll 函数不再模拟 userRepository,而直接使用原始库;此外,我们还添加 databaseModule 以创建指向数据库的连接。
与此同时,由于我们现在使用的是真实数据库,因此必须编写对应函数调整数据库以完成测试。

在测试之前与之后,我们需要清空数据库,保证不存在任何干扰内容。

另外,我们还需要手动关闭指向数据库的连接,这样才能保证测试完成后所有处理程序都被正确关闭。
通过单元测试,我们已经检查了函数能否正常工作。因此,这里可以直接测试该函数能够与 TypeORM 的 save() 方法相结合,进而将新用户对象存储在数据库内。

我们编写了名为 getOneUserFromDb 的辅助函数,它的作用顾名思义——从数据库内获取一个用户。接下来,检查邮件与 accountConfimed 属性是否正确(后者在实体类内应默认设置为 false)。

端到端测试

端到端测试是一种软件测试方法,旨在持续跟踪应用程序的整个运行流程是否与设计思路一致。

这类测试的目标,在于确保应用程序能否在真实场景下按预期方式运行。

到目前为止,我们已经测试了用户密码是否经过正确的哈希处理,以及密码及邮件是否被保存在数据库内。

现在,我们需要通过请求测试验证流程。我们的控制器内包含一条验证管道,通过测试传入的有效负载检查对象是否与 CreateUserDto 相匹配。

9d21e7fdea17fe7e30a7c87da24fba13

下面来看测试过程:

53572f2b133d284600bb70655b3f461b

在这里,我们希望通过测试观察系统在创建用户之后,是否会发送完整对象或者以错误格式发送属性。

这就是我们在某些极端情况下,使用三种基础软件测试方法得出的示例结果。

手动测试与自动测试

说到这里,我们的测试过程一直以手动方式编写——因为示例规模不大,所以过程非常顺利。但如果代码量庞大,那么测试的复杂度与工作量会急剧增加。

例如,如果需要测试身份验证系统,大家就必须复制真实用户的完整行为。另外,在测试环境的构建阶段还需要模拟请求与响应部分,包括 cookie 及其他内容。很明显,测试套件越复杂,运行需要的时间就越长。

幸运的是,自动化工具已经成为当前测试工作中的有力武器。这类工具包含多种内置功能,允许用户模拟整个测试环境,轻松搞定手动方式几乎无法实现的测试流程。

大家还可以走得更远,在应用程序中用上 API 自动测试工具。这些工具带有多种附加选项,能够高效生成负载测试、回归测试与实际运行状况等数据报告。

另外,它们还拥有良好的 UI 设计,进一步降低测试编写难度。

总结

要让软件真正为生产环境做好准备,测试绝对是不可或缺的一环。而随着应用程序复杂性的持续提升,测试工作很可能成为开发团队的最大瓶颈。

在这种情况下,请确保按照具体类型将测试套件区分开来,正如我们在示例中的做法。想要测试哪类功能,就使用与之对应的套件,保证事半功倍。

如果手动套件不足以满足用例要求,或者您发现测试太难且编写耗时太长,那么不妨选择自动化工具及平台。目前这类方案已经相当成熟,绝对能够成为各位日常工作中的好帮手。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-04-09
本文作者:Anton Lawrence
本文来自:“InfoQ”,了解相关信息可以关注“InfoQ

相关文章
|
9天前
|
API 数据库 开发者
构建高效可靠的微服务架构:后端开发的新范式
【4月更文挑战第8天】 随着现代软件开发的复杂性日益增加,传统的单体应用架构面临着可扩展性、维护性和敏捷性的挑战。为了解决这些问题,微服务架构应运而生,并迅速成为后端开发领域的一股清流。本文将深入探讨微服务架构的设计原则、实施策略及其带来的优势与挑战,为后端开发者提供一种全新视角,以实现更加灵活、高效和稳定的系统构建。
17 0
|
8天前
|
Kubernetes 安全 Java
构建高效微服务架构:从理论到实践
【4月更文挑战第9天】 在当今快速迭代与竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性及容错性,成为众多企业转型的首选。本文将深入探讨如何从零开始构建一个高效的微服务系统,覆盖从概念理解、设计原则、技术选型到部署维护的各个阶段。通过实际案例分析与最佳实践分享,旨在为后端工程师提供一套全面的微服务构建指南,帮助读者在面对复杂系统设计时能够做出明智的决策,并提升系统的可靠性与维护效率。
|
1天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
2天前
|
监控 JavaScript 安全
构建微服务架构下的API网关
【4月更文挑战第15天】在微服务架构中,API网关扮演着至关重要的角色。它作为系统的唯一入口,不仅负责请求的路由、负载均衡和认证授权,还涉及到监控、日志记录和服务熔断等关键功能。本文将探讨如何构建一个高效且可靠的API网关,涵盖其设计原则、核心组件以及实现策略,旨在为后端开发人员提供一套实用的指导方案。
14 4
|
3天前
|
监控 负载均衡 API
构建高性能微服务架构:后端开发的最佳实践
【4月更文挑战第14天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选方法。本文深入探讨了后端开发人员在设计和维护高性能微服务时需要遵循的一系列最佳实践。我们将从服务划分原则、容器化部署、API网关使用、负载均衡、服务监控与故障恢复等方面展开讨论,并结合实际案例分析如何优化微服务性能及可靠性。通过本文的阅读,读者将获得实施高效微服务架构的实用知识与策略。
|
5天前
|
运维 监控 自动驾驶
构建可扩展的应用程序:Apollo与微服务架构的完美结合
构建可扩展的应用程序:Apollo与微服务架构的完美结合
27 10
|
11天前
|
运维 负载均衡 网络协议
探索微服务架构下的服务发现机制
【4月更文挑战第6天】 随着现代软件工程的发展,微服务架构因其灵活性、可扩展性而日益受到重视。在此架构模式下,服务发现成为了确保系统高可用性和弹性的关键组件。本文将深入探讨微服务环境中服务发现的核心概念、实现方式以及面临的挑战,旨在为开发者提供一套明晰的服务发现指南和实践建议。
|
16天前
|
负载均衡 网络协议 Java
构建高效可扩展的微服务架构:利用Spring Cloud实现服务发现与负载均衡
本文将探讨如何利用Spring Cloud技术实现微服务架构中的服务发现与负载均衡,通过注册中心来管理服务的注册与发现,并通过负载均衡策略实现请求的分发,从而构建高效可扩展的微服务系统。
|
16天前
|
消息中间件 安全 API
构建高效微服务架构:策略与实践
【4月更文挑战第1天】在数字化转型的浪潮中,微服务架构已成为企业追求敏捷、可扩展和灵活部署的重要技术手段。本文将深入探讨如何通过合理的设计原则和先进的技术栈,构建一个高效的微服务系统。我们将剖析微服务设计的核心要点,包括服务的划分、通信机制、数据一致性以及安全性问题,并结合案例分析,展示如何在现实世界中应用这些策略以提升系统的可靠性和性能。
|
17天前
|
设计模式 API 持续交付
构建高效微服务架构:从理论到实践
在当今快速迭代和部署的软件开发环境中,微服务架构已成为一种流行的设计模式,它允许开发团队以模块化的方式构建、维护和扩展应用程序。本文将深入探讨微服务的核心概念,包括其定义、优势、挑战以及如何在实际项目中实施。我们将通过一个实际案例来展示如何将传统的单体应用拆分成一系列独立、松耦合的服务,并通过容器化、服务发现、API网关和持续集成/持续部署(CI/CD)等技术手段来管理这些服务。