[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 中通过提供与浏览器相同的 DOM 和 API 接口来模拟浏览器环境。
安装好之后,还需要将 jest 配置文件中的 testEnvironment 修改为 jsdom。
另外,虽然 jest-environment-jsdom 提供了一些全局对象和 API,如 window、document、XMLHttpRequest 等,但是 jest-environment-jsdom 并没有提供对 fetch 和 location 等 API 的模拟。这就需要我们手动安装 jest-fetch-mock 和 jest-location-mock 等库,来模拟这些浏览器 API 的行为。
具体来说,jest-fetch-mock 是用于模拟 fetch 函数的行为,它可以让我们在测试用例中模拟 fetch 请求,并返回指定的响应数据。而 jest-location-mock 则是用于模拟浏览器的 location 对象,它可以让我们在测试用例中设置和检查浏览器的 URL。
因此,当我们使用 jest-environment-jsdom 来模拟浏览器环境时,需要手动安装这些库来模拟 fetch和 location 等浏览器 API 的行为,以确保我们可以编写全面而准确的基于浏览器环境的测试用例。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源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