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); });
匹配器是一种可以让您测试值的方法。
依赖项是您的应用程序所依赖的一段代码。它可以是我们项目中的函数/对象或第三方依赖项(例如 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 方法本身就是一个高阶函数。
这是一个工厂方法,用于创建新的、未使用的模拟函数。
JavaScript 中的函数是一等公民,它们可以作为参数传递。
每个模拟函数都有一些特殊的属性。模拟属性是基础。此属性是一个对象,其中包含有关如何调用函数的所有模拟状态信息。该对象包含三个数组属性:
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); });
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
// 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 自动将模块的所有导出设置为 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; }
有时您只想观看一个方法被调用,但保留原始实现。其他时候,您可能想要模拟实现,但稍后在套件中恢复原始版本。
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 是单线程的: 一次只能运行一个任务。通常这没什么大不了的,但现在想象一下你正在运行一个需要 30 秒的任务.. 是的.. 在该任务期间,我们等待 30 秒,然后才会发生其他事情(JavaScript 默认在浏览器的主线程上运行,所以整个UI都卡住了)。
现在是 2020 年,没有人想要一个缓慢、反应迟钝的网站。
幸运的是,浏览器为我们提供了一些 JavaScript 引擎本身不提供的功能:Web API。这包括 DOM API、setTimeout、HTTP 请求 等。这可以帮助我们创建一些异步,非阻塞行为
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); });
Jest 通常希望同步执行测试的函数。
如果我们执行异步操作,但我们不让 Jest 知道它应该等待测试结束,则会给出误报。
import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
异步模式
JavaScript 中有多种处理异步操作的模式;最常用的是:
你不能在回调中进行测试,因为 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"); });
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); });
以上是Jest 简介:单元测试、模拟和异步代码的详细内容。更多信息请关注PHP中文网其他相关文章!