测试 Lightning Web 组件

Jest 是一个强大的工具,具有丰富的功能,可用于编写 JavaScript 测试。使用 Jest 为所有 Lightning Web 组件编写单元测试。

在命令行中运行 Jest 测试,或者在 IDE 中(通过一些配置)运行 Jest 测试。Jest 测试不会在浏览器中运行,也不会连接到组织,因此它们运行速度很快。在“监视模式”下运行时,它们会在您编码时为您提供即时反馈。Jest 测试仅适用于 Lightning Web 组件,不适用于 Aura 组件。

将 Jest 测试编写到:

  • 单独测试组件
  • 测试组件的公共 API(属性和方法、事件)@api
  • 测试基本用户交互(点击次数)
  • 验证组件的 DOM 输出
  • 验证事件是否按预期触发

提示

lwc-recipes 存储库中充满了 Jest 测试。在 的 Lightning Web 组件的文件夹中查找 Jest 测试。__tests__force-app/main/default/lwc

安装sfdx-lwc-jest

安装及其依赖项到每个 Salesforce DX 项目中。 仅适用于 Salesforce DX 项目。sfdx-lwc-jestsfdx-lwc-jest

先决条件

在安装之前,请安装 Node.js 和 npm。sfdx-lwc-jest

  • 节点.js此页面列出了 Node.js 的两个版本。我们建议使用“LTS”(长期支持)版本,而不是“当前”版本。
  • npm安装 Node.js 时,npm 也会安装。不过,您可能需要更新 npm,因此请访问 npm 网站以获取说明。

使用 Salesforce CLI 将 Jest 及其依赖项安装到您的项目中

安装 Jest 及其依赖项的最简单方法是运行 Salesforce CLI 命令 sf force lightning lwc test setup。从每个 Salesforce DX 项目的顶级目录运行命令。此命令将创建必要的配置文件并为您安装软件包。sfdx-lwc-jest

手动安装 Jest 及其依赖项

如果您在无法使用 Salesforce CLI 的环境中工作,您可以自行设置测试依赖项。

要安装及其依赖项,请从每个 Salesforce DX 项目的顶级目录运行这些命令一次。sfdx-lwc-jest

npm install
npm install @salesforce/sfdx-lwc-jest --save-dev

默认情况下,SFDX 项目在其文件块中包含这些脚本条目。如果项目文件不包含它们,请添加它们。scriptspackage.json

{
  ...
  "scripts": {
    ...
    "test": "npm run test:unit",
    "test:unit": "sfdx-lwc-jest",
    "test:unit:watch": "sfdx-lwc-jest --watch",
    "test:unit:debug": "sfdx-lwc-jest --debug",
    "test:unit:coverage": "sfdx-lwc-jest --coverage",
    ...
  },
  ...
}

运行 Lightning Web 组件的 Jest 测试

在组件开发过程中频繁或持续运行单元测试。

从 VS Code 运行测试

如果安装适用于 Visual Studio Code 的 Salesforce 扩展,则可以在 VS Code 中运行测试、调试测试和监视 Jest 文件。有关详细信息,请参阅适用于 Visual Studio Code 的 Salesforce 扩展。

在命令行上运行测试

要运行项目的所有测试,请使用 Salesforce CLI 命令 sf force lightning lwc test run from your root folder of your project。否则,请运行在安装 Jest 和 dependencies 时添加到项目文件块的命令。scriptspackage.json

npm run test:unit

在开发过程中持续运行测试

若要在每次保存更改时对单个组件运行所有测试,请转到组件目录并运行带有参数的命令。使用您在安装 Jest 和依赖项时添加到项目文件块的条目。sfdx-lwc-jest--watchscriptspackage.json

npm run test:unit:watch

Jest 会监视所有组件文件的更新,并在每次检测到更改时运行所有相关测试。

在调试模式下运行 Jests 测试

如果要逐步执行测试和应用代码,以找出测试或代码未按预期运行的原因,则在调试模式下运行 Jest 测试非常有用。您可以使用以下工具调试 Jest 测试。

  • VS Code Salesforce 扩展包
  • Chrome 开发者工具
  • VS Code 调试器高级配置

VS Code Salesforce 扩展包提供了最简单、最直接的选项,而 Chrome DevTools 则迎合了经验丰富的 Web 开发人员的需求。此外,VS Code 调试器的高级配置使你能够使用不同的调试器和调试方案。高级配置为调试 Jest 测试提供了最灵活的选项。

有关更多信息,请参阅调试 Lightning Web 组件的 Jest 测试。

高级 Jest 配置

sfdx-lwc-jest配置为运行 Jest 测试,无需任何其他更改。但是,如果您是 Jest 的高级用户,请查看 Jest 的其他一些配置选项。请参见 github.com/salesforce/sfdx-lwc-jest#overriding-jest-config

重要

如果你的 Jest 测试执行缓慢,并且它们有来自不同文件夹的依赖项,请提供映射以加快解析速度。请参阅 lwc-recipes 存储库中的 。moduleNameMapper

调试 Lightning Web 组件的 Jest 测试

VS Code 提供了一些工具来帮助你有效地调试 Jest 测试。根据您的测试要求,使用 Salesforce 扩展包、Chrome DevTools 或 VS Code 调试器高级配置。

使用 Salesforce 扩展包调试测试

适用于 Visual Studio Code 的 Salesforce 扩展提供了一个 LWC 测试侧边栏,供您轻松运行测试。可以运行一个测试用例、一个文件中的所有测试用例或项目的所有测试。有关详细信息,请参阅 LWC 测试侧栏中的功能概述。

若要调试测试,请在测试中添加断点。

例如,如果要确保正确调度事件,请单击包含方法的行号 (1)。您还可以在组件的 JavaScript 文件中的事件处理程序上添加断点,以观察事件处理程序的触发时间。dispatchEvent()

添加断点后,单击“调试测试”(2)。此链接运行以下命令:。node --inspect-brk node_modules/.bin/jest --runInBand

提示

请参阅 lwc-recipes 存储库。helloConditionalRendering.test.js

在“运行”视图中显示与运行和调试相关的信息。

若要打开“运行”视图,请单击“运行和调试”(1),您可以在其中查看表达式 (2),并使用调试工具栏 (3) 单步执行测试。在调试控制台 (4) 中,可以在测试执行时检查元素。

单击调试工具栏 (3) 上的“继续”以单步执行断点。如果未收到错误,则测试将成功完成。在 VS Code 终端中查看任何测试执行输出。

调试 Jest Salesforce 扩展

https://youtube.com/watch?v=OOGn0JLabS0

提示

有关更多调试技术,请参阅从文件内运行、调试或观看 Jest 测试。

使用 Chrome DevTools 调试测试

虽然 Salesforce 扩展包使您能够从 VS Code 中调试 Jest 测试,但 Chrome DevTools 为您提供了用于 Web 开发的全面诊断功能。

若要在调试模式下运行项目中的所有测试,请在 VS Code 终端中输入此命令。

npm run test:unit:debug

此命令调用 ,然后运行 。sfdx-lwc-jest --debugnode --inspect-brk node_modules/.bin/jest --runInBand

注意

我们建议您使用该命令,因为该命令已解析为节点依赖项,并且无法在命令行上访问。npm run test:unit:debugsfdx-lwc-jest

若要在调试模式下运行单个测试,请输入此命令,其中要调试的测试的名称。testName

npm run test:unit:debug -- testName

接下来,打开 Chrome 浏览器并转到 。“远程目标”部分列出了在调试模式下在系统中运行的所有节点进程。chrome://inspect

若要打开“DevTools”窗口,请单击“远程目标”部分中列出的“Jest 测试”下的“检查”链接。

使用“DevTools 源”面板可以调试测试。首先,在 VS Code 的测试代码中添加一条语句。debugger

注意

Chrome 检查器不会单步执行你在上一部分的 VS Code 中指定的断点。请改用测试中的语句。debugger

若要恢复测试执行,请单击 DevTools 中的“恢复脚本执行 (Resume icon)”按钮。测试将显示您的测试代码,并在您添加的语句处停止,如已编译代码的第 36 行所示。然后,可以在“源”面板中添加断点,如此屏幕截图中的第 37 行所示。debugger

提示

有关使用 Chrome DevTools 的更多调试技术,请参阅调试 JavaScript。

如果需要更好地控制如何在调试模式下运行 Jest 测试,请考虑使用 VS Code 高级配置。

使用 Chrome 开发者工具调试 Jest 测试

https://youtube.com/watch?v=TDHCrLAn33E

使用 VS Code 高级配置调试 Jest 测试

VS Code 允许你配置在调试模式下运行 Jest 测试的方式。

要使用高级配置,请执行以下操作:

  1. 生成启动配置文件。
    1. 在“运行”视图中,单击“创建 launch.json 文件”链接。从“运行和调试”选项卡生成 launch.json 文件
    2. 在显示的下拉列表中,选择“Node.js”,这将为您创建一个文件。将 的内容替换为 sfdx-lwc-jest 存储库的 Visual Studio Code 中的调试中所示的配置。launch.jsonlaunch.json注意Mac 和 Windows 用户的配置不同。按照 sfdx-lwc-jest 存储库中的说明查找操作系统的配置。若要为不同的调试器和调试方案自定义启动配置,请使用 VS Code 的 launch.json 属性。
  2. 在 Salesforce DX 项目的根目录下添加一个 Jest 配置文件。在此文件中,包括 sfdx-lwc-jest 存储库中覆盖 Jest Config 中提供的配置代码。配置代码如下所示。jest.config.jsconst { jestConfig } = require("@salesforce/sfdx-lwc-jest/config"); module.exports = { ...jestConfig, // add any custom configurations here };
  3. 在测试代码和组件的 JavaScript 文件中添加语句。debugger注意使用 VS Code 中的高级配置进行调试时,不支持添加断点。添加断点的行号与编译文件中的行号不匹配。请改用该语句。debugger
  4. 启动配置文件。转到“运行”视图。选择 Debug Jest Tests 配置,然后单击 Start Debugging (“开始调试”图标)。默认情况下,此命令运行项目中的所有测试,并在测试代码和组件的 JavaScript 文件中添加的所有调试器语句中停止。可以在 VS Code 终端中查看执行输出,并使用 VS Code 中的调试功能,例如使用调试控制台检查变量。

使用 VSCode 调试器高级配置调试 Jest 测试

https://youtube.com/watch?v=x3EbNOiczm4

提示

有关解决 Jest 问题的信息,请参阅 jestjs.io/docs/en/troubleshooting

为 Lightning Web 组件编写 Jest 测试

在本地 JavaScript 文件中编写组件测试。将它们与组件本身一起提交到版本控制中。Jest 测试不会保存到 Salesforce。

Jest 测试的编写、保存和运行方式与为 Lightning 测试服务编写的 Jasmine 或 Mocha 测试不同。Jest 测试仅是本地测试,并且独立于 Salesforce 保存和运行。

您可以使用 Salesforce CLI 命令 sf force lightning lwc test create 在目录中创建测试目录和样板测试文件。以下命令创建用于测试 Lightning Web 组件的测试目录和文件。myButton

sf force lightning lwc test create -f force-app/main/default/lwc/myButton/myButton.js

组件文件夹结构

使用 Salesforce CLI 命令后,您会看到在组件的捆绑包目录的顶层命名的文件夹,例如 .否则,请自行创建文件夹。将此组件的所有测试保存在文件夹中。通过将文件夹提交到版本控制,与其他团队成员或系统共享测试。create__tests__force-app/main/default/lwc/myButton/__tests____tests____tests__

更新 .forceignore

要确保文件夹及其内容永远不会保存到 Salesforce,请将此 glob 模式添加到每个项目的文件中。Salesforce CLI 命令 sf force lightning lwc test setup 会为您执行此任务。__tests__.forceignore

**/__tests__/**

此模式可确保推送、拉取或转换代码和元数据的 Salesforce DX 命令忽略文件夹及其内容。__tests__

测试文件命名约定

Jest 在目录中运行 JavaScript 文件。测试文件的名称必须以 结尾,我们建议测试以 结尾。可以使用一个包含所有组件测试的测试文件,也可以使用多个文件来组织相关测试。测试文件可以放在子文件夹中。__tests__.js.test.js

编写基本测试

要成为一名有成就的测试人员,请学习如何使用 Jest。特别是,学习 Jest 提供的许多匹配器的语法。我们在这里不介绍 Jest 的一般用法,因为 Jest 文档非常出色。我们专注于将 Jest 与 Lightning Web 组件一起使用的细节。

Lightning Web 组件的 Jest 测试应该单独测试单个组件的行为,对外部组件或服务的依赖性最小。

让我们看一下,这是对 lwc-recipes 存储库中 Lightning Web 组件的测试。hello.test.jshello

// hello.test.js
import { createElement } from "lwc";
import Hello from "c/hello";

describe("c-hello", () => {
  afterEach(() => {
    // The jsdom instance is shared across test cases in a single file so reset the DOM
    while (document.body.firstChild) {
      document.body.removeChild(document.body.firstChild);
    }
  });

  it("displays greeting", () => {
    // Create element
    const element = createElement("c-hello", {
      is: Hello,
    });
    document.body.appendChild(element);

    // Verify displayed greeting
    const div = element.shadowRoot.querySelector("div");
    expect(div.textContent).toBe("Hello, World!");
  });
});

让我们演练一下代码,并了解测试文件的每个部分。所有测试都必须使用此结构。

进口

首先,测试导入方法。此方法仅在测试中可用。代码还必须导入要测试的组件,在本例中为 .稍后使用这些导入来创建受测组件。createElementc/hello

import { createElement } from "lwc";
import Hello from "c/hello";

描述块

模块定义测试套件。测试套件包含一个或多个测试,从功能角度来看,这些测试属于一起。describe

describe('c-hello', () => {
  ...
});

我们建议使用一个顶级块,其描述与组件名称匹配。仅在必要时添加更多对功能进行分组的块。describedescribe

对于 ,单个就足够了。对于更复杂的组件,使用多个块将事物分组为错误场景、空输入、有线数据、常规数据等类别可能是有意义的。hello.test.jsdescribedescribe

在测试之间清理

Jest afterEach() 方法在测试结束时重置 DOM。

由于测试运行时浏览器没有运行,因此 Jest 用来提供一个行为与浏览器的 DOM 或 .Jest 依赖于 ,这是一个 Node.js 项目,因此在项目安装过程中下载的方式与 Jest 本身相同。jsdomdocumentjsdomjsdomsfdx-lwc-jest

每个测试文件共享一个 的实例,并且文件内的测试之间不会重置更改。因此,最佳做法是在测试之间进行清理,以便测试的输出不会影响任何其他测试。jsdom

afterEach(() => {
  // The jsdom instance is shared across test cases in a single file so reset the DOM
  while (document.body.firstChild) {
    document.body.removeChild(document.body.firstChild);
  }
});

Jest 还具有其他可用于执行设置和清理任务的方法。请参见 jestjs.io/docs/en/setup-teardown

IT(或测试)块

注意

it是 test 的别名。使用任何可以让您准确描述预期行为的词。

块描述单个测试。测试表示要测试的单个功能单元。编写 以描述该函数的预期行为。例如,该组件显示“Hello, World!”,因此该块会测试 hello 组件是否显示问候语。itithelloit

it('displays greeting', () => {
    ...
});

创建测试组件

该测试使用导入的方法创建要测试的组件的实例,在本例中为 .createElementc-hello

const element = createElement("c-hello", {
  is: Hello,
});

将测试组件添加到 DOM 中

然后,测试调用以将组件添加到测试的 版本 中。appendChilddocument

调用将组件插入到 DOM 和生命周期钩子中,然后被调用。appendChild()connectedCallback()renderedCallback()

document.body.appendChild(element);

下一步是使用标准的 DOM 查询方法在 DOM 中搜索元素。用作查询的父级。这是一个仅限测试的 API,可让您查看阴影边界以检查组件的阴影树。它等效于 .element.shadowRootthis.template

const div = element.shadowRoot.querySelector("div");

使用断言

最后,该语句是对成功条件的断言:元素的文本为“Hello, World!”expect

const div = element.shadowRoot.querySelector("div");
expect(div.textContent).toBe("Hello, World!");

Jest 支持许多匹配器,例如 和,这使得检查值是否满足条件变得容易。请参见 jestjs.io/docs/en/expecttoBetoMatchObject

测试异步 DOM 更新

当 Lightning Web 组件的状态发生变化时,DOM 会异步更新。若要确保测试在评估结果之前等待更新完成,请返回已解析的 Promise。将测试代码的其余部分链接到解析的 Promise。Jest 等待 Promise 链完成,然后再结束测试。如果 Promise 以拒绝状态结束,则 Jest 无法通过测试。

test("element does not have slds-icon class when bare", () => {
  const element = createElement("one-primitive-icon", { is: PrimitiveIcon });
  document.body.appendChild(element);
  // Property value is assigned after the component is inserted into the DOM
  element.variant = "bare";

  // Use a promise to wait for asynchronous changes to the DOM
  return Promise.resolve().then(() => {
    expect(element.classList).not.toContain("slds-icon");
  });
});

调用将组件插入到 DOM 和生命周期钩子中,然后被调用。然后,该示例在调用后设置元素值,这类似于将组件插入 DOM 后由另一个方法或用户交互设置属性的情况。在这些情况下,请使用 promise 等待异步 DOM 更新。有关组件生命周期和呈现的更多信息,请参见生命周期流。appendChild()connectedCallback()renderedCallback()appendChild()

如果在调用之前设置了该属性,则会同步呈现组件。在调用之前设置该属性时,无需等待异步更新或返回 promise。appendChild()appendChild()

为使用 Wire Service 的 Lightning Web 组件编写 Jest 测试

组件使用线路服务从 Salesforce 获取数据。若要测试这些组件如何处理来自线路服务的数据和错误,请使用测试实用程序。@salesforce/sfdx-lwc-jest

测试使用的输入不应依赖于外部代码或数据,您必须对其进行完全控制。从中导入测试实用程序 API 以模拟数据,以便测试不依赖于远程调用或服务器延迟等不可预测因素。sfdx-lwc-jest

该实用程序有三个适配器用于模拟线路服务数据:通用线路适配器、Lightning 数据服务 (LDS) 线路适配器和 Apex 线路适配器。sfdx-lwc-jest

  • 调用 API 时,通用适配器会按需发出数据。它不包含有关数据本身的任何额外信息。emit()
  • LDS 适配器模拟 Lightning 数据服务行为,并包含有关数据属性的信息。
  • Apex 线路适配器模拟对 Apex 方法的调用,并包含任何错误状态。

注意

如果您未使用该实用程序来使用 Salesforce DX,则可以使用测试实用程序导入适配器。但是,我们建议使用集成开发体验。sfdx-lwc-jest@salesforce/wire-service-jest-utilsfdx-lwc-jest

若要控制测试,请创建一个 JSON 文件来定义组件所需的数据,而不是从 Salesforce 获取数据。

若要使用线路服务测试实用程序,请按照以下高级步骤操作。

  1. 在组件包中,创建一个名为 的文件夹和一个名为 的 JavaScript 类。最佳做法是以组件命名文件,后跟 .__tests__componentName.test.js.test.js
  2. 在文件夹中,创建一个文件夹和一个名为 .__tests__data_wireAdapter_.json
    1. 在 JSON 文件中,模拟电线适配器发出的数据。
    2. 如果有线适配器是 LDS 有线适配器,请使用访问有线适配器所基于的 UI API 的 REST 客户端获取数据快照。这种方法比手动编写 JSON 更准确。
  3. 在文件中:componentName.test.js
    1. 导入被测元件及其电线适配器。测试必须引用与被测组件相同的电线适配器。
    2. 从 JSON 文件导入模拟数据。
    3. 发出模拟数据。
    4. 验证组件是否收到了模拟数据。

被测组件

我们的示例组件是产品卡组件。

产品卡组件显示产品及其名称。

<!-- productCard.html -->
<template>
  <div class="content">
    <template lwc:if={product}>         
        <div class="name">         
          <div>Name:</div>
          <div>{name}</div>
        </div>
    </template>
  </div>
</template>

在组件的 JavaScript 文件中,修饰器指示线路服务使用线路适配器来设置记录数据。它装饰函数,该函数接收具有错误或数据属性的对象。@wiregetRecordwiredRecord

// productCard.js
import { LightningElement, wire } from "lwc";

// Wire adapter to load records.
import { getRecord } from "lightning/uiRecordApi";

export default class ProductCard extends LightningElement {
  // Id of Product__c to display.
  recordId;

  // Product__c to display
  product;

  // Product__c field values to display
  name = "";

  @wire(getRecord, { recordId: "$recordId", fields: ["Product.Name"] })
  wiredRecord({ data }) {
    if (data) {
      this.product = data;
      this.name = data.fields.Name.value;
    }
  }
}

提示

lwc-recipes 存储库提供了使用有线服务的几个组件的示例,其中包括 Jest 测试。

模拟数据

在文件夹中,创建一个名为 的文件夹。在文件夹中,创建一个名为 的文件,该文件与电线适配器同名。定义组件期望从服务器获得的数据。您可以自己编写 JSON,但这可能会变得困难且容易出错。最佳做法是使用访问 UI API 的 REST 客户端获取数据快照。例如,此数据是来自 的响应,该响应是支持电线适配器的资源。__tests__datadatagetRecord.json/ui-api/records/{recordId}getRecord

我们的模拟数据仅定义记录中的字段。在实际测试中,模拟组件所需的所有数据。Name

{
  "fields": {
    "Name": {
      "value": "DYNAMO X1"
    }
  }
}

创建 JavaScript 测试

单元测试检查产品卡是否显示产品名称。

测试必须导入被测组件中使用的电线适配器。在这里,这是模块中的电线适配器。测试还必须导入模拟数据以通过电线适配器发送。getRecordlightning/uiRecordApi

该测试创建组件并将其附加到 DOM。仅当组件连接到 DOM 时,组件才会接收有关数据的更新。组件连接后,将模拟数据传递给电线适配器上的函数。emit

发出模拟数据后,if 或 change 组件将重新呈现。解析 promise 以确保测试代码在 DOM 更新为新数据后运行。该代码验证模拟数据中的产品名称字段是否在 DOM 中正确输出。productname

// productCard.test.js
import { createElement } from "lwc";
import ProductCard from "c/productCard";
import { getRecord } from "lightning/uiRecordApi";

// Import mock data to send through the wire adapter.
const mockGetRecord = require("./data/getRecord.json");

test("displays product name field", () => {
  const element = createElement("c-product_filter", { is: ProductCard });
  document.body.appendChild(element);

  // Emit mock record into the wired field
  getRecord.emit(mockGetRecord);

  // Resolve a promise to wait for a rerender of the new content.
  return Promise.resolve().then(() => {
    const content = element.shadowRoot.querySelector(".content");
    const nameField = mockGetRecord.fields.Name.value;
    expect(content.textContent).toBe(`Name:${nameField}`);
  });
});

注意

在 Spring ’21 及更早版本中,您必须注册正在测试的电线适配器。该代码仍然有效,但不建议这样做。

DOM 检查测试可能会发生变化

Lightning Experience 中 HTML、CSS 和 DOM 的内容和结构可能随时更改,不能被视为稳定的 API。使用 Selenium WebDriver 等工具进入组件内部的 UI 测试需要持续维护。

Salesforce 从不保证向后兼容 HTML、CSS 或 DOM。我们指出,随着 Lightning Experience 随着现代 Web 标准的发展而不断变化,这些测试的脆弱性。我们了解您从自动化 UI 测试中获得的价值,以及由此给您带来的维护负担。

Lightning Web 组件基于 Web 组件标准。该标准包括 Shadow DOM,它对其他组件隐藏组件的标记、样式和行为。这种封装给 UI 测试带来了挑战,尤其是依赖于全局搜索 DOM 或进入自定义元素内部的测试。

该属性封装元素的 DOM 子树。这在 DOM 中表示为 一个。 这个 DOM 子树中的元素不能通过传统的 DOM 查询方法获得。由 Lightning Web 组件呈现的元素包含此新属性,并且这些元素在正常的 DOM 查询中是隐藏的。shadowRootshadowRootDocumentFragmentshadowRoot

我们建议使用 Jest 对单个 Lightning Web 组件进行单元测试。

仅将 Selenium WebDriver 等 UI 测试工具用于端到端测试。

开玩笑测试

要为 Lightning Web 组件编写单元测试,请使用 sfdx-lwc-jest。

在 Jest 测试上下文中,代码可以使用被测元素的属性来访问影子树。该属性封装元素的影子树。shadowRootshadowRoot

此代码在“示例”部分访问组件的影子树。<div><lightning-lwc-parent>

const element = createElement("c-lightning-lwc-parent", { is: LightningLwcParent });
document.body.appendChild(element);
const div = element.shadowRoot.querySelector(".in-the-shadow");

Selenium WebDriver 测试

对于端到端 UI 测试,请调整现有测试以使用 Shadow DOM。调整测试因工具而异,策略也在迅速发展。本文是 Selenium WebDriver 的一个很好的例子。

正如本文所讨论的,全局查询通过失败。要在 Lightning Web 组件的影子树中查找元素,请在客户端上执行 JavaScript 以查询组件的属性。WebDriver.findElement()shadowRoot

注意

本文有一个屏幕截图,显示了 Chrome 开发者工具中的 DOM 元素。屏幕截图显示了一个文档片段,它是组件影子树的顶部节点。如果您在 Chrome 开发者工具中查看 Lightning Web 组件,则看不到 因为 LWC 使用影子 DOM polyfill。Salesforce 支持某些未实现 Shadow DOM Web 标准的浏览器。polyfill 在这些浏览器中提供了一个影子 DOM。要在页面上查找 Lightning Web 组件,请查找包含连字符的元素名称。选择元素并在控制台中运行。Lightning Web 组件返回 .#shadow-root#shadow-root$0.shadowRoot#document-fragment

Jest 测试模式和模拟依赖关系

在使用 Jest 进行测试时,请遵循这些模式和做法来隔离行为并最大限度地提高单元测试的效率。

测试属性更改

让我们从一个简单的属性更改开始。属性更改时的组件重新渲染是异步的,因此向 DOM 添加某些内容的顺序并不总是可预测的。我们建议您在检查预期行为之前,等待值更改反映在 DOM 中。一种技术使用语句在页面元素更改后检查值,如下所示:Promise.resolve()

  1. 将组件添加到 DOM。
  2. 更改属性值。
  3. 等待组件通过返回值重新呈现。Promise.resolve()
it("Renders with Hello Matt", () => {
  const element = createElement("c-hello-component", {
    is: HelloComponent,
  });
  document.body.appendChild(element);

  element.person = "Matt";

  return Promise.resolve().then(() => {
    const pTag = element.shadowRoot.querySelector("p");
    expect(pTag.textContent).toEqual("Hello, Matt!");
  });
});

提示

有关更多信息,请参阅 Matt Goldspink 在 Vlocity 撰写的博客文章 Testing Lightning Web Components。

使用属性测试组件

为了扩展我们的示例,让我们添加一个属性。属性允许您在测试中更改一个值,以查看更改的呈现方式。使用语句在调用之前设置属性,例如:Object.assign()appendChild

Object.assign(element, attributes);
document.body.appendChild(element);

例如,让我们将初始测试中的背景颜色设置为红色:

it("Renders with Hello Matt", () => {
  const element = createElement("c-hello-component", {
    is: HelloComponent,
  });
  Object.assign(component, { backgroundColor: "red" });
  document.body.appendChild(element);

  element.person = "Matt";

  return Promise.resolve().then(() => {
    const pTag = element.shadowRoot.querySelector("p");
    expect(pTag.textContent).toEqual("Hello, Matt!");
  });
});

嘲笑

单元测试可能涉及的不仅仅是对简单 UI 元素的更新。某些代码依赖于某些依赖项的行为,例如导入的模块、基本 Lightning 组件和事件处理程序。但是,您希望您的代码处于一致的环境中,不受服务器调用、数据库请求或远程访问 API 的可变行为和计时的影响。以下部分包括有关单元测试的这些依赖项的模拟行为的指南。模拟是一种常见的测试做法,用于隔离正在测试的代码的行为。

基本 Lightning 组件

Salesforce 在 sfdx-lwc-jest 存储库中提供了模拟组件,您可以在 lightning-stubs 目录中找到这些组件的源代码。将这些模拟组件用于包含基本 Lightning 组件的测试。模拟组件与实际组件的 API 匹配,但不具备所有功能,因此它们可以作为测试的良好资源。即使这些模拟组件不会触发任何事件,您仍然可以调用其事件处理程序。

例如,查看 lwc-recipes 存储库中的 miscToastNotification.js 示例。为了模拟用户输入,常量引用了 sfdx-lwc-jest 提供的模拟元素。我们仍然可以在 DOM 中查询它,并从中调度一个事件来调用我们连接到它的处理程序。我们使用一个模拟组件来保持对输入值的控制,因为我们只想测试通知的行为。inputTitleEl<lightning-input>change

// Select input field for simulating user input
const inputMessageEl = element.shadowRoot.querySelector('lightning-input[data-id="messageInput"]');
inputMessageEl.value = TOAST_MESSAGE;
inputMessageEl.dispatchEvent(new CustomEvent("change"));

在使用我们的模拟组件时,请记住以下几点。

  • 我们建议您的测试不要依赖于插槽的呈现顺序。例如,假设有两个命名槽:和 。如果存在预期内容,则即使模拟(或真实)组件交换了这些插槽的呈现顺序,测试也会通过。actionstodos
  • 基本 Lightning 组件具有一些属性,这些属性不会反映为 DOM 中的属性。例如,您可以设置属性 ,该属性用于确定要用于按钮的 SLDS 类,而不是呈现的值。设计测试用例以考虑此行为,并且不要假定所有属性都反映为属性。<lightning-button>iconPosition
  • 这些模拟不会触发任何事件,但您可以调用它们。dispatchEvent()

事件处理程序

查看相同的 miscToastNotification.js 示例,也可以看到一个模拟事件处理程序。此测试定义多个常量值,然后触发事件,将常量插入元素中。ShowToastEventNamejest.fn()lightning-input

const TOAST_TITLE = "The Title";
const TOAST_MESSAGE = "The Message";
const TOAST_VARIANT = "warning";

// Create initial element
const element = createElement("c-misc-toast-notification", {
  is: MiscToastNotification,
});
document.body.appendChild(element);

// Mock handler for toast event
const handler = jest.fn();
// Add event listener to catch toast event
element.addEventListener(ShowToastEventName, handler);

在测试结束时,验证事件处理程序是否按预期调用,以及是否使用正确的参数。

return Promise.resolve().then(() => {
  // Check if toast event has been fired
  expect(handler).toHaveBeenCalled();
  expect(handler.mock.calls[0][0].detail.title).toBe(TOAST_TITLE);
  expect(handler.mock.calls[0][0].detail.message).toBe(TOAST_MESSAGE);
  expect(handler.mock.calls[0][0].detail.variant).toBe(TOAST_VARIANT);
});

模块导入

若要创建对模拟组件的引用以更好地控制组件行为,请在文件中添加 moduleNameMapper 设置。要查看示例,请查看 lwc-recipes 存储库中的 jest.config.js 如何引用一些带有模块名称的模拟组件。jest.config.js

const { jestConfig } = require("@salesforce/sfdx-lwc-jest/config");
module.exports = {
  ...jestConfig,
  moduleNameMapper: {
    "^@salesforce/apex$": "<rootDir>/force-app/test/jest-mocks/apex",
    "^lightning/navigation$": "<rootDir>/force-app/test/jest-mocks/lightning/navigation",
    "^lightning/platformShowToastEvent$":
      "<rootDir>/force-app/test/jest-mocks/lightning/platformShowToastEvent",
    "^lightning/uiRecordApi$": "<rootDir>/force-app/test/jest-mocks/lightning/uiRecordApi",
  },
};

现在,让我们看看如何在 lwc-recipes 存储库的 miscToastNotification.test.js 中使用此配置。它有一个声明:import

import { ShowToastEventName } from "lightning/platformShowToastEvent";

如果没有该条目,此语句将解析为存根 https://github.com/salesforce/sfdx-lwc-jest/blob/master/src/lightning-stubs/platformShowToastEvent/platformShowToastEvent.js。moduleNameMapperimport

使用该条目时,此语句解析为 jest-mocks 自定义存根 https://github.com/trailheadapps/lwc-recipes/blob/master/force-app/test/jest-mocks/lightning/platformShowToastEvent.js。模拟存根具有向事件对象添加其他属性的自定义逻辑。moduleNameMapperimport

注意

如果使用自定义选项覆盖缺省文件,请将这些值与 中定义的值合并。jest.config.jssetupFilesAfterEnv@salesforce/sfdx-lwc-jest/config

@salesforce范围的导入

在测试环境中,组件可能无权访问生产架构。导入对命名空间的引用时,请在测试期间将返回值模拟为占位符。

在 Jest 测试中,我们使用 jest-transformer 将 import 语句转换为变量声明。该值设置为标签路径。默认情况下,分配的字符串值为 。您可以使用为导入提供自己的值。此示例返回字符串而不是 。@salesforce/labelmyImportc.specialLabeljest.mock()value set in testc.specialLabel

import myImport from "@salesforce/label/c.specialLabel";

jest.mock(
  "@salesforce/label/c.specialLabel",
  () => {
    return { default: "value set in test" };
  },
  { virtual: true },
);

寻找更多示例?

请参阅 lwc-recipes 存储库的 lwc 目录。许多食谱都有一个目录,其中包含注释的 jest tests。__tests__

提高性能

启用 CDN 和安全浏览器缓存以提高应用程序性能。

注意

每天第一次加载组件时,加载速度会变慢,因为该组件未缓存。在后续加载时,组件的加载和显示速度更快。

启用安全浏览器缓存

在浏览器中启用安全数据缓存,通过避免与服务器的额外往返来提高页面重新加载性能。

默认情况下,此设置处于选中状态。

注意

禁用安全和持久的浏览器缓存会对 Lightning Experience 的性能产生重大负面影响。仅在这些情况下禁用。

  • 公司的策略不允许浏览器缓存,即使数据已加密也是如此。
  • 在沙盒或 Developer Edition 中进行开发时,您希望在不清空安全缓存的情况下查看任何代码更改的效果。
  1. 在“设置”中,输入“快速查找”框,然后选择“会话设置”。Session
  2. 选中“启用安全且持久的浏览器缓存以提高性能”复选框。
  3. 点击保存

使 CDN 能够更快地加载应用程序

通过启用 Akamai 的内容分发网络 (CDN) 为 Lightning 组件框架提供静态内容,更快地加载 Lightning Experience 和其他应用程序。CDN 通常会加快页面加载时间,但它也会更改提供文件的源域。如果您的公司对 Salesforce 提供的内容有 IP 范围限制,请在启用此设置之前进行全面测试。

CDN 通过将缓存版本存储在多个地理位置来缩短静态内容的加载时间。此设置为 Lightning 组件框架中的静态 JavaScript 和 CSS 启用 CDN 交付。它不会在 CDN 中分发您的 Salesforce 数据或元数据。

默认情况下,对于在 Winter ’19 版本之前创建的组织,此设置处于禁用状态,默认情况下,对于新组织以及所有新的和现有的 Experience Builder 站点,此设置处于禁用状态。适用于 Experience Builder 站点的 Lightning CDN 无法关闭且不可配置。

  1. 在“设置”中,输入“快速查找”框,然后选择“会话设置”。Session
  2. 选中“为 Lightning 组件框架启用内容分发网络 (CDN)”复选框。
  3. 点击保存。如果您遇到任何问题,请询问您的网络管理员贵公司的防火墙是否阻止了任何 Akamai CDN 内容。确保将其添加到贵公司运营的任何允许列表或防火墙中。您可以 ping 操作,但不能直接浏览到 位于 的根 URL。https://static.lightning.force.comstatic.lightning.force.comhttps://static.lightning.force.com重要不要使用 IP 地址进行网络过滤,因为这可能会导致 的连接问题。的 IP 地址是动态的,不会在 Salesforce 的允许的 IP 地址列表中进行维护。https://static.lightning.force.comhttps://static.lightning.force.com