前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Jest实战:单元测试与服务测试

Jest实战:单元测试与服务测试

作者头像
心谭博客
发布2020-04-21 15:11:41
3.3K0
发布2020-04-21 15:11:41
举报
文章被收录于专栏:YuanXinYuanXin

需求与解决思路

一名好的大前端开发人员,一定是一名好的“配置工程师”(滑稽脸)。而最近刚到团队,被安排给 vemoJS 和 cloudbase-cli 写测试用例,并且要保证覆盖率!

这里主要以 vemojs 下的测试用例为主来讲解 Jest 要注意的地方。测试代码在:https://github.com/vemoteam/vemo/tree/master/test

观察 vemojs 这个项目,如果想进行全面测试,需要解决以下问题:

  1. 以 utils.js errror.js 等文件,对应的是单元功能测试
  2. 以 cloudbase.js 文件为代表的,需要请求远程 API,模拟不同的情况
  3. 以 index.js 中的 http 和静态服务器为代表的,测试服务是否正常启动
  4. 以 index.js 中的 websocket 服务为代表的,模拟用户使用环境,测试 ws 是否正常
  5. 提供测试覆盖率

针对以上问题,解决思路总结如下:

  1. 函数功能测试:断言匹配功能
  2. 请求 API:mock 模块和函数,例如测试用例中的 axios 就是被 mock 的
  3. http 和静态服务:测试代码中启动服务后,利用 axios 等第三方请求库请求服务
  4. websock 服务:借助 puppeteer(内置无头浏览器)来模拟用户使用,监听数据变动
  5. jest 自带覆盖率统计工具

测试过程

针对上面的步骤以及核心的 jest 配置,分别做讲解。

1. 配置文件和命令行

jest 提供两种方式来让用户自定义配置,一个是根目录的 jest.config.js ,另一个是启动 jest 的时候给参数。我是采用两者混搭的方法。

jest.config.js :在统计覆盖率的时候,忽略 testnode_modules 文件夹下。有时候为了方便,会把测试常用的函数、配置放在 test 目录下,如果不忽略,会被统计进去,但它不属于源码部分。除此之外,别忘了 node_modules,否则由于文件太多,根本启动不起来,而且结果也不对。

代码语言:javascript
复制
module.exports = {
    coveragePathIgnorePatterns: ["/test/", "/node_modules/"]
};

命令行参数写在 package.json 文件的 scripts 属性中。 需要注意的地方有 2 个, --detectOpenHandles 参数是为了当句柄未正常关闭,显式报错给用户; --env=node 指明测试环境是 nodejs,默认是浏览器。

代码语言:javascript
复制
  "scripts": {
    "test": "jest --passWithNoTests --coverage --env=node --detectOpenHandles"
  }

2. 断言与函数功能

这个很简单,但是可以配合 describe 关键字,层级区分测试逻辑。还可以配合 beforeAll 等生命周期钩子函数,提高测试效率。

代码语言:javascript
复制
const { VemoError } = require("./../src/error");

describe("error.js", () => {
    test("Throw VemoError", () => {
        function throwVemoError() {
            throw new VemoError();
        }

        expect(throwVemoError).toThrow(Error);
        expect(throwVemoError).toThrow(VemoError);
    });

    test("VemoError should have code and message", () => {
        const properties = ["code", "message"];
        const vemoError = new VemoError();
        expect(properties.every(prop => vemoError.hasOwnProperty(prop))).toBe(
            true
        );
    });
});

3. 远程 API 测试

有一些函数需要连接云的 API 进行认证,由于安全策略,不在云厂商的服务器上无法请求。这时候,就需要 mock 对应的请求库,返回我们构造好的数据,以让函数逻辑走下去,提高测试覆盖率。

代码语言:javascript
复制
jest.mock("axios");
test("getTempSecret should get tencent cloud temporary secret", async () => {
    // 下面就是mock的数据
    axios.get.mockResolvedValue({
        data: {
            TmpSecretId: "testTmpSecretId",
            TmpSecretKey: "testTmpSecretKey",
            Token: "testToken",
            ExpiredTime: Date.now()
        }
    });

    await cloudBaseMiddleware({}, async () => {});
});

4. http 与 static 服务测试

这方面很多人可能会用 supertest 这个库来测试。在做调研的时候发现,jest 的下载量和更新记录远远高于 supertest,而且更纯粹。为什么这么说呢?它提供一种测试的组织形式,其它可以借助第三方库和工具实现。

而服务测试的思路就是:在 test 目录下启动简单的 http 服务器和静态服务器,然后利用 axios 访问启动的服务器,拿到返回结果,再利用断言的写法,检查即可。

请看下面这段代码:

代码语言:javascript
复制
require("./../../src/"); // 启动服务器
// 加载配置文件和axios库
const axios = require("axios");
const config = require("./vemofile");
const instance = axios.create({
    baseURL: `http://${config.host}:${config.port}`
});
// 下面分别请求:/home#GET 和 /api#POST 接口,并且检查返回结果
describe("index.js api server", () => {
    test("template response should be HTML", async () => {
        expect.assertions(2);
        const { data, status } = await instance.get("/home");

        expect(status).toEqual(200);
        expect(data).toMatch(/<html>.*<\/html>/is);
    });

    test("post and validate check", async () => {
        expect.assertions(2);
        const params = {
            param1: "test",
            param2: 123
        };
        const { data, status } = await instance.post("/api", params);

        expect(status).toEqual(200);
        expect(JSON.stringify(data)).toEqual(JSON.stringify(params));
    });
});

5. puppeteer 与无头浏览器

针对 ws 协议,测试它的思路有点像 SSR:

  1. 启动测试后台,并且在 /ws 路由上启动 ws 协议,在 2s 后,会向链接的客户端主动发送消息
  2. puppeteer 打开新的页面,访问对应的页面,拿到页面的内容,并且记录
  3. 新的页面在等待 2s 后,接受到 /ws 主动传来的数据,然后更新页面内容
  4. 再利用 puppeteer 读取页面内容,并且记录
  5. 比较 2 次记录的内容是否有更新,如果有,那么验证通过

具体请看:https://github.com/vemoteam/vemo/blob/master/test/server/index.test.js 的 61 ~ 91 行

体验与改进

1. windows 下的 puppeteer

由于 windows 下 puppeteer 无法通过 npm 下载安装(就是很麻烦),所以把 puppeteer 的加载代码进一步处理,同时在失败的时候给出友好的提示,引导使用者切换测试平台:

代码语言:javascript
复制
// ... other codes
async function launchBrowser() {
    try {
        const puppeteer = require("puppeteer");
        const browser = await puppeteer.launch();
        return browser;
    } catch (error) {
        // if load fail, show information and return immediately
        console.log(
            "Don't run test in Windows. \n" +
                "If you fail to launch on UNIX, please install dependencies. \n" +
                "More info: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md. \n"
        );
        return null;
    }
}

const browser = await launchBrowser();
// ... other codes

2. 最小影响原则

http 服务器、静态服务器和 ws 服务器对应的启动文件 /src/index.js 没有对外暴露接口,没法显示传入要求的配置文件: vemofile.js ,它只能自动读取。

而在运行测试的时候,它会在根目录下读取 vemofile.js ,而我们的配置写在 /test/serve 下,所以要手动切换一下运行目录: process.chdir(__dirname) 。这样就保证了针对测试服务器的配置不会污染代码库。

3. 下载体验

用户在安装库的时候,显然不需要跑测试,所以需要让 npm 忽略 test 目录下的文件(其实对于一些 ts 的项目,src 下的源码也是忽略的)。给 .npmignore 添加如下内容:

代码语言:javascript
复制
# test
test

测试效果

最后放一下覆盖率统计效果吧(Ubuntu 16.04):

image.png
image.png

没覆盖的地方,全部是出现异常地方。一般来说超过 80%的覆盖率即可,其他的可以慢慢补上。这种自己手动跑的方式太 low 了,之后还会有一篇讲解 CI 等第三方工具的文章,“懒就是生产力”。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求与解决思路
  • 测试过程
    • 1. 配置文件和命令行
      • 2. 断言与函数功能
        • 3. 远程 API 测试
          • 4. http 与 static 服务测试
            • 5. puppeteer 与无头浏览器
            • 体验与改进
              • 1. windows 下的 puppeteer
                • 2. 最小影响原则
                  • 3. 下载体验
                  • 测试效果
                  相关产品与服务
                  测试服务
                  测试服务 WeTest 包括标准兼容测试、专家兼容测试、手游安全测试、远程调试等多款产品,服务于海量腾讯精品游戏,涵盖兼容测试、压力测试、性能测试、安全测试、远程调试等多个方向,立体化安全防护体系,保卫您的信息安全。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档