测试环境

刚刚的 sum 实在是太简单了,根本没难度。这一章我们来搞点有难度。

在很多时候,我们前端的代码往往只在浏览器里运行,经常要用到浏览器的 API。我之前就封装过一个 storage 文件, 通过指定 type = 'indexedDB' | 'cookie' | 'localStorage' 来切换存储的方式,而且还可以生成自定义的 key,防止全局污染。

相信大家也见过不少这种和浏览器强绑定的工具文件,那我们该如何测它们呢?

例子

对刚说的 storage 做下简化,我们只对 localStorage 进行封装,一共有 setget 两个函数。添加 src/utils/storage.ts

  1. // src/utils/storage.ts
  2. const KEY_NAME = "my-app-";
  3. const set = (key: string, value: string) => {
  4. localStorage.setItem(KEY_NAME + key, value);
  5. };
  6. const get = (key: string) => {
  7. return localStorage.getItem(KEY_NAME + key);
  8. };
  9. const storage = {
  10. get,
  11. set,
  12. };
  13. export default storage;

然后在 tests/utils/storage.test.ts 添加这个文件的测试用例:

  1. // tests/utils/storage.test.ts
  2. import storage from "utils/storage";
  3. describe("storage", () => {
  4. it("可以缓存值", () => {
  5. storage.set("newKey", "hello");
  6. expect(localStorage.getItem("my-app-newKey")).toEqual("hello");
  7. });
  8. it("可以设置值", () => {
  9. localStorage.setItem("my-app-newKye", "hello");
  10. expect(storage.get("newKey")).toEqual("hello");
  11. });
  12. });

由于 Node.js 环境并没有 localStorage,所以你会得到这样的报错:

测试环境 - 图1

全局 Mock

既然没有 localStorage,那我们可以给它 Mock 一个!首先添加 tests/jest-setup.ts 文件,然后放置 localStorage 的 Mock 实现:

  1. // tests/jest-setup.ts
  2. Object.defineProperty(global, 'localStorage', {
  3. value: {
  4. store: {} as Record<string, string>,
  5. setItem(key: string, value: string) {
  6. this.store[key] = value;
  7. },
  8. getItem(key: string, value: string) {
  9. return this.store[key];
  10. },
  11. removeItem(key: string) {
  12. delete this.store[key];
  13. },
  14. clear() {
  15. this.store = {}
  16. }
  17. }
  18. })

::: tip 得益于刚刚配置的 TypeScript,这里的 setup 文件也可以写成 .ts 了! :::

然后在 jest.config.js 里添加 setupFilesAfterEnv 配置:

  1. module.exports = {
  2. setupFilesAfterEnv: ['./tests/jest-setup.ts'],
  3. };

::: warning 推荐:使用 setupFilesAfterEnv 而不是 setupFiles :::

设置了之后,jest-setup.ts 会在每个测试文件执行前先执行一次。 相当于每执行一次测试,都会在全局添加一次 localStorage 的 Mock 实现。 现在再来执行一次 npm run test,会发现执行成功:

测试环境 - 图2

setupFilesAfterEnv vs setupFiles

插入一下:相信很多人都知道 Jest 的 setupFiles,但不太了解 setupFilesAfterEnv,这里简单讲讲它们的区别 (可从 官网的介绍 了解更多)

测试环境 - 图3

简单来说:

  • setupFiles 是在 引入测试环境(比如下面的 jsdom)之后 执行的代码
  • setupFilesAfterEnv 则是在 安装测试框架之后 执行的代码

具体应用场景是:在 setupFiles 可以添加 测试环境 的补充,比如 Mock 全局变量 abcd 等。而在 setupFilesAfterEnv 可以引入和配置 Jest/Jasmine(Jest 内部使用了 Jasmine) 插件。

如果你试图在 setupFiles 添加 Jest 的扩展/插件,那么你可能会得到 expect is not defined 报错。详见这个 Issue

为了简便,本教程把初始化代码都放在 setupFilesAfterEnv 中。在真实项目中,大家再按自己的需求来对应做配置即可。

jsdom 测试环境

回到主题,像上面 Mock LocalStorage 这样有点傻,因为我们不可能把浏览器里所有的 API 都 Mock 一遍,而且不可能做到 100% 还原所有功能。因此,jest 提供了 testEnvironment 配置:

  1. module.exports = {
  2. testEnvironment: "jsdom",
  3. }

添加 jsdom 测试环境后,全局会自动拥有完整的浏览器标准 API。原理是使用了 jsdom 。 这个库用 JS 实现了一套 Node.js 环境下的 Web 标准 API。 由于 Jest 的测试文件也是 Node.js 环境下执行的,所以 Jest 用这个库充当了浏览器环境的 Mock 实现。

现在清空 jest-setup.ts 里的代码,直接 npm run test 也会发现测试成功:

测试环境 - 图4

::: warning 请不要把 jest-setup.ts 删掉,后面还大有用处! :::

testEnvironment 除了 jsdom 还有没有别的呢?有,不过一般都只是 jsdom 的扩展环境,在下一章会讲到,那现在我们就进入下一章的学习吧~

总结

这一章里,我们学到了 setupFilesAfterEnv,它可以指定一个文件,在每执行一个测试文件前都会跑一遍里面的代码。在这个 setup 文件中, 可以放置全局的 Mock 实现,以及一些初始化操作。

为了方便测试浏览器环境下的代码,我们可以配置 testEnvironment: 'jsdom' 来创建一个 Node.js 的浏览器环境。这样我们就不用每个 API 都 Mock 一次了。