在前端开发中,我们经常需要撰写单元测试来确保代码的质量和可靠性。Jest 是一个流行的 JavaScript 单元测试框架,它提供了很多强大的功能和灵活的 API 使得测试变得更加简单和高效。然而,在 Jest 中执行网络请求是一个常见的问题,特别是使用 fetch 请求的场景。在本文中,我们将探讨 Jest 中 fetch 网络请求的测试问题,并分享如何解决它们。
问题描述
在执行 Jest 测试时,单元测试通常会在 Node.js 环境下运行,而不是在浏览器中。这就意味着测试环境中没有浏览器提供的全局 fetch API。为了在测试中模拟 fetch 网络请求,我们通常会使用一些第三方的模块,例如 node-fetch、jest-fetch-mock 和 fetch-mock 等,这样我们就能够在测试中模拟 HTTP 请求并获得响应数据。但是,在使用这些模块时,我们会遇到一些常见的问题,例如模拟 fetch 请求无效,模拟 response 数据结构错误等等。
下面我们将通过一个简单的示例代码来说明这些问题:
// fetchTest.js const fetchData = async (url) => { const res = await fetch(url); const data = await res.json(); return data; }; module.exports = fetchData;
这是一个通过 fetch 请求获取 JSON 数据的例子,我们现在需要对其进行单元测试。首先,安装 node-fetch 和 jest-fetch-mock 模块:
npm install node-fetch jest-fetch-mock --save-dev
然后在测试用例中使用 jest-fetch-mock 来截获和模拟 fetch 请求和响应:
-- -------------------- ---- ------- ------------------- ----- - -------- - - ---------------------- ----- --------- - ----------------------- ------------------- ---------- -- -- - ---------- ----- ---- ---- ----- ----- -- -- - ----- ---- - -- --- -- ----- ----- --- ----- -------- - --- ------------------------------- ------------ - -------------------------------------- ----- ------ - ----- ----------------------- ----------------------------- --- ---
根据我们的预期,测试应该成功执行并且 result 应该等于 data。但是,实际上我们会遇到一些问题,例如测试用例失败,模拟 fetch 请求无效,模拟 response 数据结构错误等。
解决方案
1. 使用 node-fetch instead of fetch-mock
第一种解决方案是使用 node-fetch 模块而非 jest-fetch-mock,这可以更好地模拟浏览器环境下的 fetch API。node-fetch 提供了可用于在 Node.js 环境中进行网络请求的 API,使用 node-fetch 的示例代码如下:
-- -------------------- ---- ------- -------------- ----- ----- - ---------------------- ----- --------- - ----- ----- -- - ----- --- - ----- ----------- ----- ---- - ----- ----------- ------ ----- -- -------------- - ----------
在测试用例中,我们不再需要 mockResolvedValue 方法,而是使用 node-fetch 模块去实际访问远程 API。示例代码如下:
-- -------------------- ---- ------- ------------------- ----- --------- - ----------------------- ------------------- ---------- -- -- - ---------- ----- ---- ---- ----- ----- -- -- - ----- ---- - -- --- -- ----- ----- --- ------------ - ---------- -- ----------------- ----- -- -- ---------------------- -- -- ----- ------ - ----- ----------------------- ----------------------------- --- ---
这样的话,我们可以完美地在测试用例中执行网络请求,并根据我们的预期结果来断言结果。在使用 node-fetch 模块时,如果遇到代理、Cookie 或跨域等问题,可以使用 nock 模块来模拟和 mock。
2. 使用 fetch-mock 将 fetch mock 掉
第二种解决方案是使用 fetch-mock 模块来模拟 fetch 的响应。fetch-mock 是一个流行的模块,它提供了可以完全替代原生 fetch API 的 API,可以让我们更灵活地处理网络请求和响应。使用 fetch-mock 的示例代码如下:
//fetchTest.js const fetchData = async (url) => { const res = await fetch(url); const data = await res.json(); return data; }; module.exports = fetchData;
在测试用例中,我们需要先使用 fetchMock.mock 方法将 fetch 函数 mock 掉并模拟响应,示例代码如下:
-- -------------------- ---- ------- ------------------- ----- --------- - ----------------------- ----- --------- - ---------------------- ------------------- ---------- -- -- - ---------- ----- ---- ---- ----- ----- -- -- - ----- ---- - -- --- -- ----- ----- --- --------------------------- - ----- ---- --- ----- ------ - ----- ----------------------- ----------------------------- -------------------- --- ---
这里我们使用 fetchMock.mock 方法将 '/api/data' 所有的 HTTP 请求 mock 掉,并返回一个响应对象,它的 body 属性等于我们预期的 data 数组。当然,在调用 fetchMock.mock 方法时,如果需要更多的配置选项,可以参考 fetch-mock 的官方文档。
3. 将 window.fetch 赋值给 global.fetch
第三种解决方案是在测试用例中将 window.fetch 赋值给 global.fetch,这样就可以在测试用例中使用原生的 fetch API 进行网络请求。示例代码如下:
//fetchTest.js const fetchData = async (url) => { const res = await fetch(url); const data = await res.json(); return data; }; module.exports = fetchData;
在测试用例中,我们需要先将 window.fetch 赋值给 global.fetch,并在测试过程中使用原生的 fetch API 进行网络请求,示例代码如下:
-- -------------------- ---- ------- ------------------- ----- --------- - ----------------------- ------------------- ---------- -- -- - ---------- ----- ---- ---- ----- ----- -- -- - ----- ---- - -- --- -- ----- ----- --- ------------ - ------------- ----- -------- - --- ------------------------------ - ------- ---- -------- - --------------- ------------------ -- --- ------------------ ------------------------------------- ----- ------ - ----- ----------------------- ----------------------------- --------------------------- --- ---
这里我们使用 jest.spyOn 方法来监控 global.fetch 方法,并在测试用例中使用原生的 fetch API 来进行网络请求。当然,这种方案要求我们的测试用例运行在支持 fetch API 的浏览器中,可以在 jest-puppeteer 或 Jest 浏览器执行器中运行。这种方案对与项目中使用 window.fetch 而非 fetch 的情况来说是比较友好的。
总结
Jest 是一个强大的 JavaScript 单元测试框架,它提供了很多强大的功能和灵活的 API 使得测试变得更加简单和高效。然而,在 Jest 中执行网络请求是一个常见的问题,特别是使用 fetch 请求的场景。在本文中,我们讨论了 Jest 中 fetch 网络请求测试的若干问题,并提供了三种方案来解决这些问题。在实际的项目中,我们可以根据具体的情况选择相应的方案来测试网络请求,这样可以确保我们的单元测试更加完善和可靠。
关于本文的示例代码,它们也可以在我的 GitHub 仓库中找到:https://github.com/xxjwxc3/front_end_study/tree/main/Jest%20Fetch%20%E6%B5%8B%E8%AF%95。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64eecc23f6b2d6eab38c00b1