[Jest] 浏览器环境下的测试

这一小节我们的目标是学习 Jest 针对在浏览器环境下面的代码,特别是使用到了浏览器 Api 的代码,如何进行测试。

示例一

示例一:有一个输入框,用户在输入框中输入内容,该内容会被存储到 localstorage 里面。localstorage 就是浏览器环境下面特有的 Api

<body>
    <p>请输入你要存储的值</p>
    <div>
        <input type="text" name="content" id="content">
        <button id="saveBtn">存储</button>
        <button id="getBtn">获取</button>
    </div>
    <div style="margin-top:10px">存储的值为:<span id="username"></span></div>
    <script src="./js/index.js" type="module"></script>
</body>
// index.ts
import storage from "./storage.js";

// 获取 DOM 元素
const saveBtn = document.getElementById("saveBtn") as HTMLButtonElement;
const getBtn = document.getElementById("getBtn") as HTMLButtonElement;
const content = document.getElementById("content") as HTMLInputElement;
const username = document.getElementById("username") as HTMLSpanElement;

saveBtn.addEventListener("click", function () {
  storage.set("user", content.value);
  content.value = "";
  window.alert("存储成功");
});

getBtn.addEventListener("click", function () {
  const value = storage.get("user");
  if (value !== null) {
    username.innerHTML = value;
  } else {
    username.innerHTML = "";
  }
});

// storage.ts

/**
 * 专门负责存储内容到 localstorage 的工具库
 */

const KEY = "my-app-";

/**
 * 负责存储
 */
function set(key: string, value: string) {
  localStorage.setItem(KEY + key, value);
}

/**
 * 负责获取
 */
function get(key: string) {
  return localStorage.getItem(KEY + key);
}

export default {
  get,
  set,
};

上面的代码,有那么几个注意点:

  • index.html 在引入模块的时候,需要写成 js, 并且script的type应该是module
  • typescript 在进行编译的时候,我们要将 ts 的配置文件里面的 module 从 commonjs 修改为 ES20xx

接下来我们就要对我们的代码进行一个测试。

首先我们通过 npx jest --init 生成一个 jest 的配置文件,注意在选择配置项目的时候,环境要选择 jsdom,因为我们的代码是在浏览器环境运行的,会涉及到一部分只有在浏览器环境才有的 api

  testEnvironment: "jsdom", // 提供浏览器的api
  // testEnvironment: "jest-environment-node", // 只有node的api

 // A preset that is used as a base for Jest's configuration
  preset: "ts-jest",

接下来我们来针对 storage 里面提供的工具函数进行测试。

书写如下的测试用例:

import storage from "../ts/storage";

describe("测试storage存储", () => {
  // 测试存储
  test("测试存储", () => {
    storage.set("newKey", "Hello");
    expect(localStorage.getItem("my-app-newKey")).toBe("Hello");
  });

  // 测试获取
  test("测试获取", () => {
    localStorage.setItem("my-app-newKey", "World");
    expect(storage.get("my-app-newKey")).toBe("World");
  });
});

需要注意,在上面我们生成 jest 配置文件的时候,环境选择了 jsdom,如果还是按照以前选择 node 环境的话,这里会出现一个问题:

ReferenceError: localStorage is not defined

之所以出现这个问题,是因为在 nodejs 环境中并不存在 localstorage,localstorage 是浏览器环境下特有的 api。

除此之外,从 jest28 版本开始你还需要安装一个依赖:jest-environment-jsdom,由这个依赖来提供特有的像 localstorage、window全局对象之类的 api。核心作用就是在 nodejs中模拟出浏览器的环境。

示例二

我们经常还需要一些额外的依赖库来晚上在 node.js 中模拟浏览器环境的情况。

/**
 * 工具函数库
 */

// 该函数的作用是将 url 后面的查询字符串转为对象
const getSearchObj = () => {
  // ?a=1&b=2
  const { search } = window.location;

  // a=1&b=2
  const searchStr = search.slice(1);

  // ['a=1', 'b=2']
  const pairs = searchStr.split("&");

  // { 'a': '1' }
  const searchObj: Record<string, string> = {};

  pairs.forEach((pair) => {
    // [a, 1]
    const [key, value] = pair.split("=");
    searchObj[key] = value;
  });

  return searchObj;
};

export default {
  getSearchObj,
};

该函数的作用是将 url 后面的查询字符串转为对象:

window.location.href = "https://www.baidu.com?a=1&b=2";
const result = getSearchObj();
// result ---> {a:'1', b: 2}

接下来我们书写对应的测试代码,来对 getSearchObj 进行一个测试,测试代码如下:

import tools from "../ts/tools";
const { getSearchObj } = tools;

describe("测试getSearchObj", () => {
  // 测试是否正常返回对象
  test("测试是否正常返回对象", () => {
    window.location.href = "https://www.baidu.com?a=1&b=2";
    const result = getSearchObj();
    expect(result).toEqual({
      a: "1",
      b: "2",
    });
  });
});

接下来我们会发现这个测试用例跑不通,会报:Error: Not implemented: navigation (except hash changes) 这样的一个错误

之所以会报这个错,是因为虽然我们使用了 jest-environment-jsdom去模拟浏览器环境的 API 接口,但是有一部分 API 是缺失的。比如上面例子的 location,在 jest-environment-jsdom 中就没有提供。

所以这个时候我们需要一些额外的库,例如在当前的例子里面,我们就可以安装 jest-location-mock。

修改上面的测试用例代码,如下:

import tools from "../ts/tools";
import "jest-location-mock"
const { getSearchObj } = tools;

describe("测试getSearchObj", () => {
  // 测试是否正常返回对象
  test("测试是否正常返回对象", () => {
    // window.location.href = "https://www.baidu.com?a=1&b=2";
    window.location.assign("https://www.baidu.com?a=1&b=2");
    const result = getSearchObj();
    expect(result).toEqual({
      a: "1",
      b: "2",
    });
    expect(window.location.search).toBe("?a=1&b=2");
  });

  // 测试参数为空的时候
  test("测试参数为空的时候",()=>{
    window.location.assign("https://www.baidu.com");
    const result = getSearchObj();
    expect(result).toEqual({});
    expect(window.location.search).toBe("");
  });
});

示例三

再比如,我们的浏览器环境下面还有 fetch,这个也是 nodejs 环境没有的。

首先,我们书写了如下的工具方法:

// 和服务器通信获取数据
const fetchData = (id: number) => {
  return fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
    .then((res) => res.json())
    .then((res) => res);
};

然后我们这针对这个方法进行测试,书写了如下的测试用例:

import tools from "../ts/tools";
const { fetchData } = tools;

describe("测试fetchData", () => {
  // 测试返回的数据是否有对应的属性
  test("测试返回的数据是否有对应的属性", async () => {
    const result = await fetchData(1);
    expect(result).toHaveProperty("userId");
    expect(result).toHaveProperty("id");
    expect(result).toHaveProperty("title");
    expect(result).toHaveProperty("completed");
  });

  // 测试返回的数据对应的值是否正确
  test("测试返回的数据对应的值是否正确", async () => {
    const result = await fetchData(1);
    expect(result).toEqual({
      userId: 1,
      id: 1,
      title: "delectus aut autem",
      completed: false,
    });
  });
});

但是在跑测试用例的时候,我们发现报错了,报错信息为:ReferenceError: fetch is not defined

原因和上一个例子是类似的,即便我们安装了 jest-environment-jsdom,但是有一部分 api 是缺失的,例如这里的 fetch 也是没有的。

这里我们可以安装一个额外的库:jest-fetch-mock

安装好这个库之后,我们可以将上面的测试用例进行一个修改,添加一条如下的语句:

import "jest-fetch-mock";

添加了上面的语句后,整个测试用例就能够跑通了。

总结

当我们所书写的代码是要在浏览器环境下面运行时,代码里面可能会涉及到很多浏览器相关的 Api,此时需要安装 jest-environment-jsdom,该库在 Node.js 中通过提供与浏览器相同的 DOMAPI 接口来模拟浏览器环境。

安装好之后,还需要将 jest 配置文件中的 testEnvironment 修改为 jsdom

另外,虽然 jest-environment-jsdom 提供了一些全局对象和 API,如 window、document、XMLHttpRequest 等,但是 jest-environment-jsdom 并没有提供对 fetchlocationAPI 的模拟。这就需要我们手动安装 jest-fetch-mockjest-location-mock 等库,来模拟这些浏览器 API 的行为。

具体来说,jest-fetch-mock 是用于模拟 fetch 函数的行为,它可以让我们在测试用例中模拟 fetch 请求,并返回指定的响应数据。而 jest-location-mock 则是用于模拟浏览器的 location 对象,它可以让我们在测试用例中设置和检查浏览器的 URL。

因此,当我们使用 jest-environment-jsdom 来模拟浏览器环境时,需要手动安装这些库来模拟 fetchlocation 等浏览器 API 的行为,以确保我们可以编写全面而准确的基于浏览器环境的测试用例。

posted @   Zhentiw  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2021-01-26 [Serverless] Netlify serverless Demo
2021-01-26 [React] useState with Typescript
2018-01-26 [Webpack + React] Import CSS Modules with TypeScript and webpack
2018-01-26 [Angular] Short Imports with TypeScript Path Mapping
2017-01-26 [Node.js] Build microservices in Node.js with micro
2017-01-26 [Angular] Scrolling the Message List To the Bottom Automatically Using OnChanges
2017-01-26 [Angular] Ngrx/effects, Action trigger another action
点击右上角即可分享
微信分享提示