第九部分:打卡系统:测试:后端测试
这篇文章是我撰写的一系列文章的一部分,旨在介绍打卡系统。如果您想了解更多信息,可以阅读以下文章:
- 第一部分:打卡系统:示意图。
- 第 2 部分。打卡系统:基本后端 — AuthModule。
- 第 3 部分。打卡系统:基本后端 — 用户模块。
- 第 4 部分。打卡系统:基本后端 - AppModule。
- 第五部分:打卡系统:种子数据库和迁移数据
- 第 6 部分. 打卡系统:基本前端。
- 第 7 部分。打卡系统:使用 docker/docker-compose 部署后端 (nestJS)。
- 第 8 部分。打卡系统:使用环境部署前端(Angular 2+)。
- 第 9 部分:测试:后端测试 — 单元测试 — 服务
- 第十部分:测试:后端测试——单元测试——控制器
- 第 11 部分:测试:后端测试——端到端测试
- 第 12 部分:测试:前端测试——单元测试
- 第 13 部分:测试:前端测试——集成测试
介绍
这是我关于测试的第一篇博文,也可以说是关于质量评估(QA)的第一篇博文。这个项目并非从一开始就采用测试驱动开发(TDD),但我目前正在进行测试阶段。得益于测试阶段,我发现了很多小bug,如果项目已经上线,这些bug可能会造成很大的问题。但实际上,项目将在接下来的几周内正式上线。这些测试对于修复在此期间发现的几个bug将非常有帮助。
测试的第一步是决定测试什么。任何人都可能会告诉你,你必须测试整个应用程序,并且必须达到接近 100% 的覆盖率,但实际上你并不需要测试整个应用程序,而只需要测试软件中最关键的部分。这些关键部分的覆盖率可能接近 90% 或 70%,具体取决于你的应用程序。
在本例中,我将说明我们应该进行哪些测试:
- 服务:
-
app.service.
-
用户服务。
-
身份验证服务。
- 控制器:
-
app.controller。
-
用户.控制器。
因此,在我们的项目中,没有必要对 DTO、常量、实体和模块进行测试,因为这些测试很难,而且价值很小。
后端采用 NestJS 框架开发,并使用 Jest 作为测试工具。此外,NestJS 还包含一个强大的测试包,可以模拟类似于 Angular 测试包的环境。
服务测试
在这篇文章中,我将介绍服务单元测试。这类测试是测试金字塔中最简单的测试。我建议测试新手从服务单元测试入手,因为服务是功能单一、易于隔离的小函数。正因如此,它们也是最简单、最容易测试的。
应用服务
我们要测试的第一个服务是 app.service.ts,它使用了两个服务:AuthService 和 UserService。因此,我们的测试套件必须检查 app.service 是否能使用正确的参数调用这两个服务。
第一步是为每个待开发的测试进行初始配置。因此,`app.service.ts` 的构造函数需要两个服务(`AuthService` 和 `UserService`),它们将作为间谍对象。`@nestjs/testing` 中的 `Test` 包提供了 `createTestingModule` 方法,用于创建一个测试模块。在这个 `testingModule` 中,`providers` 数组由 `AppService` 和两个使用工厂创建的间谍对象组成。以下代码展示了此初始配置:
下一步是确定我们要测试的内容。主要思路是独立测试每个函数/方法。因此,以下方法是 app.service.ts 代码的一部分。
authIn 和 authOut 方法应该检查 authService 是否使用正确的参数调用。在本例中,这是一个单元测试,因此不应该使用实际的函数/方法调用 this.authService.authIn 和 this.authService.authOut 方法,这就是我们使用 spy 来监控这些方法的原因。测试这些函数的代码如下:
在之前的测试中,您可以注意到期望值与 `authIn` 和 `authOut` 方法相关,这些方法用于检查这些方法是否被调用以及参数是否正确。在这些方法中,`authIn` 或 `authOut` 方法抛出的错误与此无关,因为这些方法中的职责已委托给其他服务。
与 usersTicketing 方法相关的测试如下:
在这种情况下,我们创建了一个间谍对象,用于在执行 `now from Date` 函数时进行测试。在这种情况下,测试结果必须始终返回同一天(测试必须是纯函数,不能依赖于外部因素)。因此,在这个测试中,我们需要检查 `getUsersMustBeWorkingNow` 方法是否已被调用,以及 `usersTicketing` 方法的返回值是否为一个对象,该对象包含键为 `users` 的对象,其值为间谍对象 `UserService` 中提供的值,时间戳为模拟日期的时间戳。
用户服务
测试用户服务的步骤与 app.service.ts 中使用的步骤相同。因此,第一步是创建测试模块,其中包含将在后续测试中使用的 spy 和服务。
第一种方法非常简单,因为它使用的技术与 app.service.ts 中的技术相同。因此,要测试的代码如下:
它的测试套件只检查是否使用正确的参数(用户原型和初始参数)调用了 save 方法,如下面的代码所示:
接下来要测试的方法是调用 TypeORM ORM,如下所示:
在这个测试中,我们需要使用链式责任来监视 usersRepository 中的每个方法。为此,我们使用 Jest 提供的工厂方法。
如您所见,我们正在检查 TypeORM 中每个被调用的方法以及调用时使用的参数,简单快捷。
以下方法可能存在一个著名的代码异味(方法过长),但如果您仔细阅读该方法,您会发现它很好地调用了数据库查询,并且代码本身并没有代码异味。
查询语句有多种参数组合,但测试内容相同,因此我们需要一个包含输入输出的表格来进行测试。Jest 提供了一个名为 each 的参数,可用于参数化我们的测试。
表格如下:
您可以看到,我们表格中用于测试的参数如下:
-
年份:与我们想要测试用户是否在建筑物内的时间对应的年份。
-
月份:与我们想要测试用户是否在建筑物内的时间对应的月份。
-
day:与我们想要测试用户是否在建筑物内的时刻对应的日期。
-
小时:与我们想要测试用户是否在建筑物内的时刻对应的小时数。
-
分钟:对应于我们要测试用户是否在建筑物内的时刻的分钟数。
-
秒:对应于我们想要测试用户是否在建筑物内的时刻的秒数。
-
hourNowExpected:小时,应该使用其他参数列表返回该方法。
-
dayNowExpected:应该使用其他参数列表返回该方法的日期。
我们的测试需要大量的间谍函数来测试 ORM,并使用表中的预期值来检查私有方法是否返回了 ORM 查询所需的值。如果私有方法是公共的,测试会更容易,但测试绝不应该修改原始代码(除非发现 bug)。
测试的第一部分是创建间谍程序,以检查是否使用了正确的参数调用该方法。然后,调用 `service.getUsersMustBeWorkingNow()` 方法。最后,还有一系列预期任务,用于检查 ORM 的方法是否使用了正确的参数调用。
因此,该测试的最终代码如下:
身份验证服务
最后一个要测试的服务是 auth.service.ts。测试方法与之前的测试类似。因此,第一步是在每个测试中进行初始配置。
待测试的代码如下:
您可以看到,有一些私有方法无法直接测试,因为它们类似于将代码复制粘贴到公共方法中。因此,这些方法没有测试套件。
私有方法如下:
在我们的 authIn 和 authOut 方法测试套件中,有三个不同的测试,代表了一种场景,如下所示。
- 身份验证。
-
应该具备身份验证功能并返回问候语。
-
当找不到用户时,应返回错误。
-
当出现意外错误时,应返回错误信息。
- authOut。
-
应该保存身份验证信息并返回再见。
-
当找不到用户时,应返回错误。
-
当出现意外错误时,应返回错误信息。
身份验证代码如下:
authOut 代码如下:
结论
本文介绍了如何使用 Jest 和 NestJS 框架测试后端服务。这段代码最有趣的地方在于,我们可以使用 spy 来隔离测试用例,并创建一个输入输出表,从而自动化执行许多相同但参数不同的测试。
下一篇文章中,我将向您展示如何对控制器进行单元测试。
-
GitHub 项目地址为https://github.com/Caballerog/clock-in-out。
-
本文的 GitHub 分支是https://github.com/Caballerog/clock-in-out/tree/part9-backend-unit-test。
原文发表于www.carloscaballero.io,日期为 2019 年 3 月 15 日。
文章来源:https://dev.to/carlillo/part-9-clock-in-out-system-testing-backend-testing-3imk


















