本文首发于:https://github.com/bigo-frontend/blog/ 欢迎关注、转载。

如何写好eggjs单元测试

前言

笔者在平时面试前端同学时,经常遇到候选人有nodejs开发经验,但是很少有编写单元测试。
希望写下这篇文章,让大家多重视单元测试,交付高质量的代码。

如果你的项目单元测试分支规范率达到80%以上,我就认为这个同学的代码质量意识特别好。

为什么要单元测试

如测试金字塔,单元测试是底座。

引用eggjs官网的话猛戳这里

  • 你的代码质量如何度量?
  • 你是如何保证代码质量?
  • 你敢随时重构代码吗?
  • 你是如何确保重构的代码依然保持正确性?
  • 你是否有足够信心在没有测试的情况下随时发布你的代码?

如果答案都比较犹豫,那么就证明我们非常需要单元测试。

特别是大型nodejs项目,经过多年的代码迭代,业务逻辑复杂,代码改动很容易牵一发动全身,单元测试就能给应用的稳定性提供了一层保障。不用面对qa的灵魂拷问:为什么老是你的bug最多!

测试准备

eggjs提供了很好的测试模块:egg-mock,通过egg-mock/bootstrap,可以快速实例化app

// test/controller/home.test.js
const { app, mock, assert } = require('egg-mock/bootstrap');describe('test/controller/home.test.js', () => {// test cases
});

自定义mockServiceByData与getMockData

但是我们知道,要写单测,对mock数据比较依赖,需要我们准备大量的json数据,故在app.mockService基础上拓展了mockServiceByData与getMockData方法

1.新建test/global.ts

注:如果是bigo内网,可以import bigoMock from ‘@bigo/bgegg-mock’;

import * as assert from 'assert';
import { app } from 'egg-mock/bootstrap';
import * as path from 'path';
import * as fs from 'fs';class BigoMock {app;ctx;assert = assert; // 挂载assertasync before() {console.log('hello bigoMock');this.app = app;await app.ready();this.ctx = app.mockContext();return;}/*** 模拟 Service 方法返回值* @param service 方法类* @param methodName 方法名* @param fileName 文件名(状态)*/mockServiceByData(service, methodName, fileName) {let serviceClassName = '';if (typeof service === 'string') {const arr = service.split('.');serviceClassName = arr[arr.length - 1];}const servicePaths = path.join(serviceClassName, methodName);this.app.mockService(service, methodName, () => {return this.getMockData(servicePaths, fileName);});}/*** 获取本地test/mockData的mock数据* @param folder 文件夹* @param fileName 文件名*/getMockData(folder, fileName) {return this.getJson(folder, fileName);}/*** 约定从test/mockData/service/methodName/fileName.json获取数据* @param folder 文件夹* @param fileName 文件名*/getJson(folder, fileName) {// 默认追加json后缀console.log(path.extname(fileName));if (!path.extname(fileName)) {fileName = fileName + '.json';}const fullPaths = path.join(process.cwd(), 'test/mockData', folder, fileName);return fs.readFileSync(fullPaths, 'utf-8');}
}const bigoMock = new BigoMock();
(async function() {await bigoMock.before();
})();export default bigoMock;

2.mockServiceByData

import bigoMock from './../global';describe('user接口单测用例', () => {it('should mock fengmk1 exists', () => {// 返回test/mockData/user/get/success.jsonbigoMock.mockServiceByData('user', 'get', 'success.json');return app.httpRequest().get('/user?name=fengmk1').expect(200)// 返回了原本不存在的用户信息.expect({name: 'fengmk1',});});});

3.getMockData

import bigoMock from './../global';// TESTS=test/app/service/spider/githubIssues/index.test.ts npm test
describe('githubIssues爬虫单测用例', () => {it('解析html结构成功', async () => {// 返回test/mockData/githubIssues/html_mock.jsconst html = bigoMock.getMockData('githubIssues', 'html_mock.js');const result = bigoMock.ctx.service.spider.githubIssues.index.getLinks(html);bigoMock.assert(result[0].title === 'nginx反向代理实现线上调试');bigoMock.assert(result[0].href === 'https://github.com/bigo-frontend/blog/issues/3');});});

编写Service单测

如果编写controller单测,从用户请求到达 ==》 返回ctx.body数据,这个过程会涉及Controller、Service、以及下游接口调用等环节。经过的分支逻辑太多,数据会有很多中间状态,这样要准备的单测用例就特别复杂,导致单测分支覆盖率低。但是Service就不一样了,每个Service函数都是单一功能,有明确的输入、输出结果,只要我们的service单元测试代码足够多,单测覆盖率自然就上去了。

当然应用的 Controller、Helper、Extend 等代码,都必须也有对应的单元测试保证代码质量。

综上,本文会重点讲service单测。

如何执行单个测试文件

我们知道执行 npm run test (实际执行 egg-bin test),就会跑全部的测试用例,但是我们通常编写单测时,只关心当前单测的执行情况。
我们可以在命令行执行如下命令,执行指定测试文件

TESTS=test/app/service/spider/githubIssues/index.test.ts npm test

如果我们一个单测文件的测试用例很多,只希望跑一个用例,可以使用it.only

import bigoMock from './../../../../global';// TESTS=test/app/service/spider/githubIssues/index.test.ts npm test
describe('githubIssues爬虫单测用例', () => {it('解析html结构成功', async () => {const html = bigoMock.getMockData('githubIssues', 'html_mock.js');const result = bigoMock.ctx.service.spider.githubIssues.index.getLinks(html);bigoMock.assert(result[0].title === 'nginx反向代理实现线上调试');bigoMock.assert(result[0].href === 'https://github.com/bigo-frontend/blog/issues/3');});// 只会执行该用例it.only('解析html结构失败', async () => {const html = '';const result = bigoMock.ctx.service.spider.githubIssues.index.getLinks(html);bigoMock.assert(result.length === 0);});});

注:在提交代码前,记得移除only,否则执行npm run test时,只会执行该用例

如何写好eggjs单元测试相关推荐

  1. 如何写好 eggjs 单元测试

    点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 来源:bigo大魔王 https://juejin.cn/post/6949084159801294855 如何写好eggjs单元 ...

  2. nodejs异常处理过程/获取nodejs异常类型/写一个eggjs异常处理中间件

    前言 今天想写一下eggjs的自定义异常处理中间件,在写的时候遇到了问题,这个错误我捕获不到类型?? 处理过程,不喜欢看过程的朋友请直接看解决方法和总结 看一下是什么: 抛出的异常是检验失败异常Val ...

  3. python测试代码怎么写_Python 单元测试

    Test your software, or your users will. "Test ruthlessly. Don't make your users find bugs for y ...

  4. 如何写出优秀的单元测试

    单元测试已是软件工程师必备的技能,但在我的经验中,有些人写的单元测试实际上却没测到重点,而且还容易因为重构而导致测试失败,可说是为了测试而测试.这样的测试不仅不会带来好处,反而还使专项更不稳健,因此遵 ...

  5. 如何写好测试用例以及go单元测试工具testify简单介绍

    背景 ​ 最近在工作和业余开源贡献中,和单元测试接触的比较频繁.但是在这两个场景之下写出来的单元测试貌似不太一样,即便是同一个代码场景,今天写出来的单元测试和昨天写的也不是很一样,我感受到了对于单元测 ...

  6. 前端单元测试怎么写(以Vue为例)

    单元测试是什么 对软件中的最小可测试单元(一个方法)进行测试 单元测试的意义 1.分模块开发,方便定位到哪个模块出现问题 2.保证了代码质量 3.驱动开发(先写单元测试,通过再写代码) 单元测试两种类 ...

  7. java如何写单元测试_java如何使用JUnit进行单元测试

    注:所有内容都是在eclipse上实现,关于eclipse的安装和jdk的安装配置,请看:http://www.cnblogs.com/fench/p/5914827.html 单元测试是什么? 百度 ...

  8. pringboot 单元测试 空指针_单元测试中的 FIRST 原则

    单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证.进行单元测试,可以尽早地发现编写代码中错误,减少后期测试开销和维护成本,提高软件质量. 下文讲解写出好单元测试需要遵守 ...

  9. 单元测试Struts2的Action(包含源码)

    很久没有从头搭建Struts2的环境了.最近,认真实践了单元测试Struts2.Spring等Java项目. 今天特意写的是单元测试Struts2的Action,遇到了不少问题,果然是实践出真知啊. ...

最新文章

  1. 二级域名用asp.net 2.0的实现方案
  2. XDOC Office Server 开源了,Office文档完美转换为PDF
  3. ACMNO.26 C语言-字符统计2 编写一函数,由实参传来一个字符串,统计此字符串中字母、数字、空格和其它字符的个数,在主函数中输入字符串以及输出上述结果。 只要结果,别输出什么提示信息。
  4. XtraGrid GridView设置默认选中的行颜色
  5. 基于Linux+Nagios+Centreon+Nagvis等构建海量运维监控系统
  6. ajax发送post请求_按键精灵安卓版发送post和get请求
  7. junit与testng 分别和mockito 结合使用例子
  8. tkinter 菜单添加事件_Tasker的最新测试劫持了Android 11的电源菜单
  9. HDU5977-Garden of Eden-树分治+FWT
  10. php与数据库的连接用法 (签到一)
  11. python用pip安装numpy完整命令_Python使用pip安装Numpy模块
  12. mysql mycont,MySQL与DevC++的连接问题
  13. Android——基于监听器的事件处理(转)
  14. plsql日期转换错ORA-01830
  15. Ubuntu各文件夹功能说明
  16. python怎么读单词和古文_Python 实现文言文词频统计
  17. 4246. 【五校联考6day2】san (Standard IO)
  18. linux下 不显示光驱,Windows7电脑下不显示光驱盘符的解决方法
  19. 全面了解Qtum节点端口配置,立即加入全节点激励计划
  20. 视频转换格式该如何操作

热门文章

  1. 一文读懂华为Mate20系列三大看点
  2. linux centos7 完整邮件服务器搭建及调用_2018_lcf
  3. PHP intl扩展实现汉字转拼音
  4. 使用Python给pdf进行加密
  5. whatsapp 官网入口一款非常流行的社交软件
  6. 聊一聊磁珠在电路中的作用
  7. 远程服务器接收json,从远程服务器解析JSON数据
  8. c语言递归法解释,给我解释一下C语言递归函数?
  9. Python实战:爬取小说《盗墓笔记》
  10. 大学计算机基础码,大学计算机基础字符的编码——区位码和国标码