Jest 简介:单元测试、模拟和异步代码
笑话简介
Jest 是一个用于测试 JavaScript 代码的库。
这是一个由 Facebook 维护的开源项目,它特别适合 React 代码测试,但不仅限于此:它可以测试任何 JavaScript 代码。它的优点是:
- 速度很快
- 它可以执行快照测试
- 它固执己见,提供开箱即用的一切,无需您做出选择
export default function sum(a, n) { return a + b; }
divide.test.js
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
匹配器
匹配器是一种可以让您测试值的方法。
- toBe 比较严格相等,使用 ===
- toEqual 比较两个变量的值。如果它是一个对象或数组,它会检查所有属性或元素的相等性
- 传递 null 值时 toBeNull 为 true
- 传递定义值时 toBeDefined 为 true(与上面相反)
- 传递未定义值时 toBeUndefine 为 true
- toBeCloseTo 用于比较浮点值,避免舍入错误
- toBeTruthy true 如果该值被认为是 true (就像 if 那样)
- toBeFalsy 如果该值被认为是假(就像 if 一样),则为 true
- 如果 Expect() 的结果高于参数 ,则 toBeGreaterThan true
- toBeGreaterThanOrEqual 如果 Expect() 的结果等于参数或高于参数 ,则为 true
- 如果 Expect() 的结果低于参数 ,则 toBeLessThan true
- toBeLessThanOrEqual 如果 Expect() 的结果等于参数或低于参数 ,则为 true
- toMatch 用于将字符串与正则表达式模式匹配进行比较
- toContain 用于数组,如果预期数组在其元素集中包含参数 ,则为 true
- toHaveLength(number):检查数组的长度
- toHaveProperty(key, value):检查对象是否具有属性,并可选择检查其值
- toThrow 检查您传递的函数是否抛出异常(一般情况下)或特定异常
- toBeInstanceOf():检查对象是否是类的实例
依赖关系
依赖项是您的应用程序所依赖的一段代码。它可以是我们项目中的函数/对象或第三方依赖项(例如 axios)
当您自己的应用程序没有一段代码就无法运行时,它就成为真正的依赖项。
例如,如果您在应用程序中实现一项功能来发送电子邮件或发出 api 请求或构建配置对象等
有两种方法我们可以在js项目的代码中添加依赖项:
进口
export default function sum(a, n) { return a + b; }
依赖注入
只是一个简单概念的奇特术语。
如果您的函数需要外部依赖项的某些功能,只需将其作为参数注入即可。
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
单元测试
单元测试由软件开发人员编写和运行,以确保应用程序的一部分(称为“单元”)满足其设计并按预期运行。
我们想要单独测试我们的代码,我们不关心任何依赖项的实际实现。
我们想要验证
- 我们的代码单元按预期工作
- 返回预期结果
- 按其应有的方式调用任何协作者(依赖项)
这就是模拟我们的依赖关系发挥作用的地方。
嘲笑
在单元测试中,模拟为我们提供了存根依赖项所提供的功能的能力,以及意味着观察我们的代码如何与依赖项交互。
当将依赖项直接包含到我们的测试中成本昂贵或不切实际时,例如,当您的代码对 API 进行 HTTP 调用或与数据库层交互时,模拟特别有用。
最好删除这些依赖项的响应,同时确保它们按要求被调用。这就是模拟派上用场的地方。
通过使用模拟函数,我们可以知道以下内容:
- 收到的来电数量。
- 参数 每次调用时使用的值。
- 每次调用时的“上下文”或这个值。
- 函数如何退出以及产生了哪些值。
开玩笑地嘲笑
创建模拟函数有多种方法。
- jest.fn 方法允许我们直接创建一个新的模拟函数。
- 如果您正在模拟对象方法,则可以使用 jest.spyOn。
- 如果你想模拟整个模块,你可以使用 jest.mock。
jest.fn 方法本身就是一个高阶函数。
这是一个工厂方法,用于创建新的、未使用的模拟函数。
JavaScript 中的函数是一等公民,它们可以作为参数传递。
每个模拟函数都有一些特殊的属性。模拟属性是基础。此属性是一个对象,其中包含有关如何调用函数的所有模拟状态信息。该对象包含三个数组属性:
- 调用 [每次调用的参数]
- 实例 [每次调用时的“this”值]
-
Results [函数退出的值],results 属性有类型(return 或 throw)和值
- 函数显式返回一个值。
- 函数运行完成,没有 return 语句(相当于返回 undefined
- 函数抛出错误。
export default function sum(a, n) { return a + b; }
- https://codesandbox.io/s/implementing-mock-functions-tkc8b
模拟基础
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
模拟注入的依赖项
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
模拟模块
使用 jest.fn 模拟函数
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
这种类型的嘲笑不太常见,原因如下:
- jest.mock 自动为模块中的所有函数执行此操作
- jest.spyOn 做同样的事情,但允许恢复原始功能
使用 jest.mock 模拟模块
更常见的方法是使用 jest.mock 自动将模块的所有导出设置为 Mock 函数。
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
使用 jest.spyOn 监视或模拟函数
有时您只想观看一个方法被调用,但保留原始实现。其他时候,您可能想要模拟实现,但稍后在套件中恢复原始版本。
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
恢复原来的实现
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
JavaScript 和事件循环
JavaScript 是单线程的: 一次只能运行一个任务。通常这没什么大不了的,但现在想象一下你正在运行一个需要 30 秒的任务.. 是的.. 在该任务期间,我们等待 30 秒,然后才会发生其他事情(JavaScript 默认在浏览器的主线程上运行,所以整个UI都卡住了)。
现在是 2020 年,没有人想要一个缓慢、反应迟钝的网站。
幸运的是,浏览器为我们提供了一些 JavaScript 引擎本身不提供的功能:Web API。这包括 DOM API、setTimeout、HTTP 请求 等。这可以帮助我们创建一些异步,非阻塞行为
export default function sum(a, n) { return a + b; }
- 调用堆栈 - 当我们调用一个函数时,它会被添加到称为调用堆栈的东西中。
- WebAPI - setTimeout 由 WebAPI 提供,采用回调函数并设置计时器,而不阻塞主线程
- 队列 - 当计时器结束时,回调被添加到队列
- 事件循环 - 检查调用堆栈是否为空,检查队列中是否有要执行的回调,然后移至要执行的调用堆栈
import sum from './sum'; // Describe the test and wrap it in a function. it('adds 1 + 2 to equal 3', () => { const result = sum(1, 2); // Jest uses matchers, like pretty much any other JavaScript testing framework. // They're designed to be easy to get at a glance; // here, you're expecting `result` to be 3. expect(result).toBe(3); });
使用 Jest 测试异步代码
Jest 通常希望同步执行测试的函数。
如果我们执行异步操作,但我们不让 Jest 知道它应该等待测试结束,则会给出误报。
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
异步模式
JavaScript 中有多种处理异步操作的模式;最常用的是:
- 回调
- Promise 和异步/等待
测试回调
你不能在回调中进行测试,因为 Jest 不会执行它 - 测试文件的执行在回调被调用之前结束。要解决此问题,请将参数传递给测试函数,您可以方便地调用“done”。 Jest 将等到您调用 done() 后再结束测试:
// Constructor Injection // DatabaseManager class takes a database connector as a dependency class DatabaseManager { constructor(databaseConnector) { // Dependency injection of the database connector this.databaseConnector = databaseConnector; } updateRow(rowId, data) { // Use the injected database connector to perform the update this.databaseConnector.update(rowId, data); } } // parameter injection, takes a database connector instance in as an argument; easy to test! function updateRow(rowId, data, databaseConnector) { databaseConnector.update(rowId, data); }
承诺
对于返回 Promise 的函数,我们从测试中返回一个 Promise:
// 1. The mock function factory function fn(impl = () => {}) { // 2. The mock function const mockFn = function(...args) { // 4. Store the arguments used mockFn.mock.calls.push(args); mockFn.mock.instances.push(this); try { const value = impl.apply(this, args); // call impl, passing the right this mockFn.mock.results.push({ type: 'return', value }); return value; // return the value } catch (value) { mockFn.mock.results.push({ type: 'throw', value }); throw value; // re-throw the error } } // 3. Mock state mockFn.mock = { calls: [], instances: [], results: [] }; return mockFn; }
异步/等待
为了测试返回 Promise 的函数,我们还可以使用 async/await,这使得语法非常简单明了:
test("returns undefined by default", () => { const mock = jest.fn(); let result = mock("foo"); expect(result).toBeUndefined(); expect(mock).toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith("foo"); });
尖端
- 我们需要很好地理解我们的函数的作用以及我们要测试的内容
- 思考我们正在测试的代码的行为
- 设置舞台:
- 模拟/监视任何依赖项
- 我们的代码是否与全局对象交互?我们也可以嘲笑/监视他们
- 我们的测试是否与 DOM 交互?我们可以构建一些假元素来使用
- 构建你的测试
- 鉴于...
- 当我打电话时......
- 然后...我期待......
const doAdd = (a, b, callback) => { callback(a + b); }; test("calls callback with arguments added", () => { const mockCallback = jest.fn(); doAdd(1, 2, mockCallback); expect(mockCallback).toHaveBeenCalledWith(3); });
链接
- https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c
- https://jestjs.io/docs/en/mock-functions
- https://codesandbox.io/s/implementing-mock-functions-tkc8b
- https://github.com/BulbEnergy/jest-mock-examples
- https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
- https://jestjs.io/docs/en/asynchronous
- https://www.pluralsight.com/guides/test-asynchronous-code-jest
以上是Jest 简介:单元测试、模拟和异步代码的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

JavaScript是现代Web开发的核心语言,因其多样性和灵活性而广泛应用。1)前端开发:通过DOM操作和现代框架(如React、Vue.js、Angular)构建动态网页和单页面应用。2)服务器端开发:Node.js利用非阻塞I/O模型处理高并发和实时应用。3)移动和桌面应用开发:通过ReactNative和Electron实现跨平台开发,提高开发效率。

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

本文展示了与许可证确保的后端的前端集成,并使用Next.js构建功能性Edtech SaaS应用程序。 前端获取用户权限以控制UI的可见性并确保API要求遵守角色库

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务
