概述
在前端测试中,Mock 测试是一种非常重要的测试类型。Mock 测试的主要目标是针对某个系统组件进行单元测试,但是该组件所依赖的其他组件却被 Mock 掉,以消除对这些组件的依赖,从而实现快速测试。这种测试方法在前端开发中尤其常见,因为前端的组件化和模块化趋势较为明显。
Jest 是一个非常流行的前端测试框架,其有很好的 Mock 功能,可以用来测试前端代码中的 API 接口、组件以及其他 JavaScript 代码等。在开发过程中,我们需要深入了解 Jest 的 Mock 测试功能,以便写出高效且易于维护的测试用例。本篇文章将详细介绍 Jest 框架开启 Mock 测试的正确姿势。
Jest 中的 Mock
Jest 提供了两种 Mock 方式:手动 Mock 和自动 Mock。
手动 Mock 是指在测试代码中手动编写 Mock 函数。当我们需要 Mock 其他模块中的某个函数或对象时,我们可以通过 import/export 语法和 jest.mock() 函数手动模拟这个模块。手动 Mock 非常灵活,可以对被测试的函数、模块进行精细化的 Mock,但是需要我们手动编写 Mock 代码,较为费时费力。
自动 Mock 则是指 Jest 在执行测试时,会自动将依赖于被测试模块的函数(防止出现副作用)变为 Mock 函数。这种 Mock 方式使测试代码更简洁,但是却稍微失去了一些灵活性。我们需要清楚使用场景,选择适合自己的 Mock 方式。
接下来,我们将详细介绍如何在 Jest 中使用手动 Mock,包括 Mock 模块、Mock 函数和模拟 Promise 请求等技巧。
手动 Mock 模块
- Mock 模块的导出
当我们需要 Mock 模块的某个函数时,我们需要Mock整个模块或者只 Mock 数个函数的导出,以便在测试过程中替换真正的依赖项。
Mock 整个模块的方式如下:
-- -------------------- ---- ------- -- --------- -------------- - - ---------- -- -- - -- --------- - - -- ------- --------------------- -- -- - ------ - ---------- ------------------------------- -------- - -- -- -------- ----------- --- ---- ---------- --- -- ------- --------- --
之后我们在测试文件中就可以通过 import 模块后直接使用被 Mock 的 getConfig 函数了。
Mock 数个函数的导出,我们可以利用 ES6 模块导出机制中的 export/import 实现。例如我们有两个函数 f1 和 f2,我们希望在测试代码中 Mock 它们,则可以这样子。
-- -------------------- ---- ------- -- --------- ------ ----- -- - -- -- - -- --- - ------ ----- -- - -- -- - -- --- - -- ------- ------ - --- -- - ---- ------------- ------------------------ -- -- -- --- ------------------------------- -- --------- --- ------------------------------- -- -------- ---
- Mock 匿名箭头函数
匿名函数和箭头函数经常用来定义一些回调函数。在前端开发中,经常会遇到需要 Mock 匿名箭头函数的场景。
因为 Jest 的 Mock API 只能基于接口(即对象的属性)。我们必须使用 jest.fn() 来创建 Mock 函数,但是箭头函数无法赋值给变量或属性。这时候我们可以使用自执行函数自定义变量上下文来 Mock 匿名箭头函数。比如有底下这段代码:
const myClick = jest.fn() const button = document.createElement('button') button.onclick = () => { myClick() } document.body.appendChild(button)
对于 Mock 匿名箭头函数,我们可以这样子:
const myClick = jest.fn() const button = document.createElement('button') button.onclick = (() => { const arrowClick = jest.fn() return arrowClick })() document.body.appendChild(button)
Mock 一个函数
我们经常需要 Mock 一个函数,这是 Mock 测试中的常见需求。
下面是我们建议使用的流程:
- 声明要 Mock 的函数并导出它。例如我们要 Mock './module.js' 中的 func 该如何实现?
export const func = (...args) => { // 真正的 func 实现逻辑 }
- 编写测试代码,使用 jest.fn() 函数定义 Mock。
-- -------------------- ---- ------- ------ - -- ------ ---- ------------- ------------ ------ -- -- - ------------ ---- ---- --- ------ ------- -- -- - -- ---- ---- -- ----------- - ---------------------------------- -- ---------- ----- ------ - -------------- ------------------------------- -------------------------------------- -- --
如上,我们直接 Mock 了 './module.js' 中的 func,并改变了其返回值。由于在测试代码的上下文环境中,所有导出的函数都被 import 带入,我们可以轻松地在测试代码中定义 Mock。
模拟 Promise 请求
Jest Mock 可以帮助我们 Mock 异步请求,下面是示例代码:
-- -------------------- ---- ------- ------ - --------- - ---- ------------- -------------- ----------- -- -- - -------- - ----- ------ ------ -- - ------------ -- - ------ -- -- -- -- ------------ ------ ----- ---- ------- ------ ----- --------- - ---------- -- - -------------------------------------------------------- -------------- -- - ----------------------- -- -
在上述代码中,我们使用了 done 函数来进行异步测试的 promiseMock,这只是其中的一种写法。但下面的是 Jest 中更方便的写法,也更加优雅:
-- -------------------- ---- ------- ------ ----- ---- ------- ------ - --------- - ---- ------------- ------------------ --------------------- -- -- - ---------- ------ ------ -- -- - ----- ---- - - ----- - -------- ---- - - --------------------------------- ------ ------------------------- -- - ---------------------------------------- -- -- --
我们通过 axios.get.mockResolvedValue(data) 来将请求成功的结果 data 改写,以确认 fetchData 是否返回预期的 response。这种 Mock Promise 的写法比较常见,大致流程如下:
- 用 jest.mock() 和 jest.fn() 创建 Mock。
- 调用测试流程中的 promise 并做出假设。
- 最后使用 expect() 校验该 promise 返回的结果。
总结
本篇文章详细介绍了手动 Mock 模块、Mock 函数和模拟 Promise 请求等 Jest Mock 的基本操作。我们希望通过这篇文章,能够让读者更深入了解 Jest 框架的 Mock 测试功能,更加熟练地掌握 Jest 的 Mock API,在前端单元测试中更加高效、精准地 Mock 函数和模块,从而完成质量更高、适用性更强的前端开发工作。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/645818aa968c7c53b0a8f7a8