闪电Web组件测试 – 写一个Jest测试

学习目标

完成本单元后,您将能够:

  • 编写测试以验证您的设置。
  • 编写失败的测试,然后更改组件以使其通过。
  • 识别基本的Jest命令。
  • 解释生命周期挂钩。

从Lightning Web组件开始

要测试Lightning Web组件,我们首先必须有一个要测试的组件。

创建一个闪电Web组件

  1. 在Visual Studio Code中,通过按Ctrl + Shift + P (Windows)或Cmd + Shift + P (macOS)打开命令面板。
  2. 输入lightning web
  3. 选择SFDX:创建Lightning Web组件。不要使用SFDX:创建闪电组件。(这将创建一个Aura组件。)
  4. 输入unitTest新组件的名称。
  5. Enter键
  6. 再次按Enter接受默认值force-app/main/default/lwc

这将在lwc目录中使用初始基本文件创建unitTest目录。

test-lwc项目中的unitTest目录。

编写基本测试

玩笑测试的编写,保存和运行方式与为Aura Components的闪电测试服务编写的茉莉或摩卡测试不同。Jest测试仅在本地进行,并且独立于Salesforce保存和运行。实际上,如果尝试将Jest测试部署到Salesforce组织中,则会出现错误。尽管针对Lightning Web组件的Jest测试未部署到您的Salesforce组织中,但是请确保将它们与组件本身一起提交到版本控制。 

创建__tests__文件夹

测试文件需要与其他组件文件分开。在组件的bundle文件夹的顶层创建一个名为__tests__的文件夹。将此组件的所有测试保存在__tests__文件夹中。 

  1. 在Visual Studio Code中,右键单击unitTest目录,然后选择“新建文件夹”
  2. 输入__tests__
  3. Enter键

配置.forceignore

通过将__tests__文件夹提交到版本控制,与其他团队成员或系统共享测试。它们是您的项目和持续集成过程的重要组成部分。为了避免将它们部署到Salesforce,.forceignore文件为此输入了一个排除项。 

  • 确保您的项目.forceignore文件包含以下排除项。如果没有,请添加它并保存文件。
    # LWC configuration files
    **/jsconfig.json
    **/.eslintrc.json
    # LWC Jest
    **/__tests__/**
     

创建Jest测试文件

我们的第一个测试很简单。我们有一个sum()函数,它希望将两个数字相加并作为参数传递给它。

  1. 在Visual Studio Code中,右键单击__tests__目录,然后选择New File
  2. 输入sum.test.js
  3. Enter键
  4. 在新的测试文件中输入以下代码:
    import { sum } from '../sum';
      
    describe('sum()', () => {
      it('should add 1 and 2 returning 3', () => {
        expect(sum(1, 2)).toBe(3);
      });
    });
     
  5. 保存文件。

运行测试

  1. 在Visual Studio Code中,选择“查看” ,然后选择“终端”。这将在Visual Studio Code中打开一个终端。终端默认为当前项目的顶级目录。
  2. 在终端中,从上一个单元执行以下命令:
    npm run test:unit
     
  3. 由于缺少求和函数,测试失败。

让我们看看如何解决它。 

  1. 在Visual Studio Code中,右键单击unitTest目录,然后选择New File
  2. 输入sum.js
  3. Enter键
  4. 在新文件中输入以下代码块:
    export function sum(x, y) {
      return x + y;
    }
     
  5. 保存文件。
  6. 在终端中再次运行测试:
    npm run test:unit
     
  7. 测试通过。


恭喜你!您刚刚确认Jest已安装并正常工作。

让我们看一下测试代码,看看发生了什么。

import { sum } from '../sum';
  
describe('sum()', () => {
  it('should add 1 and 2 returning 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
});
 
  • 第1行sum从sum JavaScript文件导入导出的函数。
  • 第3行是Jest测试套件的开始。该describe函数或块是一个测试套件,并接受两个参数。首先是对我们要测试的单位的描述,通常以名词的形式出现。第二个是保存一个或多个测试的回调函数。您也可以将describe测试套件相互嵌套以提高清晰度。
  • 第4行是测试(it是的别名test)。该it函数或块也接受两个参数。首先是对我们期望的另一种描述,通常以动词开头。然后是一个建立测试并保存测试的断言或期望的回调函数。
  • 第5行是expect声明成功条件的语句:该sum函数将添加两个参数1和2,并返回3。它toBe是许多 Jest匹配器之一。
    在第5行之后的行中添加另一个断言:
        expect(sum(1, 2)).not.toBeGreaterThan(3);
     
  • 添加.not.toBeGreaterThan确保该数字不大于3。您可以使用添加另一个Expect语句.not.toBeLessThan(3)。 

现在进行Lightning Web组件测试。

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

再次执行该过程以创建unitTest测试文件

此测试将验证是否设置了属性,并将其添加到DOM后将显示正确的文本。

  1. 右键单击__tests__目录,然后选择“新建文件”
  2. 输入unitTest.test.js
  3. Enter键
  4. 在新的测试文件中输入以下代码:
    import { createElement } from 'lwc';
    import UnitTest from 'c/unitTest';
      
    describe('c-unit-test', () => {
      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 unit status with default unitNumber', () => {
        const element = createElement('c-unit-test', {
          is: UnitTest
        });
        expect(element.unitNumber).toBe(5);
        // Add the element to the jsdom instance
        document.body.appendChild(element);
        // Verify displayed greeting
        const div = element.shadowRoot.querySelector('div');
        expect(div.textContent).toBe('Unit 5 alive!');
      });
    });
     
  5. 保存文件。
  6. 在终端中再次运行测试:
    npm run test:unit
     
  7. 测试失败:
    Test Suites: 1 failed, 1 passed, 2 total
    Tests:       1 failed, 1 passed, 2 total
     

在更新代码以获得通过测试之前,让我们看一下测试代码以了解需求。

  • 第1行是新的。它createElementlwc框架导入方法。仅在测试中可用。
  • 第2行UnitTest从组件JavaScript控制器导入该类。
  • 第4行开始describe测试套件块。
  • 第5行是Jest清理方法。afterEach()是Jest的设置和清除方法之一。afterEach()在描述块中的每个测试之后运行。此afterEach()方法在测试结束时重置DOM。测试运行时,Jest没有运行浏览器。Jest使用jsdom来提供行为类似于浏览器的DOM或文档的环境。每个测试文件都有一个jsdom实例,并且不会在文件内部的测试之间重置更改。最佳实践是在测试之间进行清理,以使测试的输出不会影响任何其他测试。还有其他可用的设置和清除方法。检查资源。
  • 第12行启动it测试块。
  • 第13行是您使用导入createElement方法的位置。它创建组件的实例并将其分配给常量element
  • 第16expect行将断言unitNumber变量设置为5。这是我们要测试的第一个要求,首先unitNumber设置为5。
  • 第18行实际上使用将该element代码添加到jsdom的版本中。该调用将Lightning Web组件附加到DOM并进行渲染,这也意味着将调用生命周期挂钩connectedCallback()和renderedCallback()(稍后会对此进行更多介绍)。document.bodyappendChild
  • 第20行使用querySelector(标准DOM查询方法)在DOM中搜索div标签。使用element.shadowRoot作为父的查询。这是一个仅测试的API,可让您窥视阴影边界以检查组件的阴影树。
  • 最后,第21行有expecttextContent的的div标签来断言“单元5还活着!” 在那儿。这是最终要求。声明文本正确。

要使测试通过,您需要向unitTest HTML和JavaScript文件中添加代码。我们将添加代码以满足要求。

  1. 单击该unitTest.html文件将其打开。
  2. 覆盖unitTest.html为:
    <template>
      <lightning-card title="Unit Status" icon-name="standard:bot">
        <div class="slds-m-around_medium">
          Unit {unitNumber} alive!
        </div>
      </lightning-card>
    </template>
     
  3. 保存文件。
  4. 单击该unitTest.js文件将其打开并用以下命令覆盖:
    import { LightningElement, api } from 'lwc';
    import { sum } from './sum';
      
    export default class UnitTest extends LightningElement {
      @api unitNumber = sum(2,3);
    }
     
  5. 保存文件并运行测试:
    npm run test:unit
     
  6. 所有测试通过。

测试异步DOM更新

当Lightning Web组件的状态更改时,DOM异步更新。为确保您的测试在评估结果之前等待更新完成,请返回已解决的Promise。为此,请将其余测试代码链接到已解决的Promise。Jest在结束测试之前等待Promise链完成。如果Promise以拒绝状态结束,则Jest无法通过测试。

  1. 打开unitTest.test.js
  2. 在最后一个测试之后添加第二个测试。
    在此测试中,我们要验证属性更改将更新DOM中的文本。
      it('displays unit status with updated unitNumber', () => {
        const element = createElement('c-unit-test', {
         is: UnitTest
        });
        // Add the element to the jsdom instance
        document.body.appendChild(element);
        // Update unitNumber after element is appended
        element.unitNumber = 6
        const div = element.shadowRoot.querySelector('div');
        // Verify displayed unit status
        expect(div.textContent).toBe('Unit 6 alive!');
      });
     
  3. 保存文件并运行测试。
    npm run test:unit
     
  4. 您收到以下失败消息:
    Expected: "Unit 6 alive!"
    Received: "Unit 5 alive!"
     

这是怎么回事?该expect声明断言div.textContext应当为“ Unit 6 live”,但仍为“ Unit 5 live!”。为了查看更改,我们需要通过返回resolve来等待它Promise。 

  1. expect在注释之后,将失败的语句替换为以下内容// Verify display unit status
        expect(div.textContent).not.toBe('Unit 6 alive!');
        // Return a promise to wait for any asynchronous DOM updates. Jest
        // will automatically wait for the Promise chain to complete before
        // ending the test and fail the test if the promise rejects.
        return Promise.resolve().then(() => {
          expect(div.textContent).toBe('Unit 6 alive!');
        });
     
  2. 使用与上次相同的命令来运行测试,或者使用上一单元的“运行Jest测试”部分中的其他选项之一。
  3. 测试通过。

到现在为止还挺好。您在两个测试套件中有三个成功的测试。接下来,添加第四项测试,以便在输入字段更新时可以验证单元状态是否已更新。为此,请在输入字段中使用change事件。

  1. 打开unitTest.test.js,如果它不是已经打开。
  2. 在您添加的最后一个测试之后添加一行,并将此第三个测试添加到套件中:
      it('displays unit status with input change event', () => {
        const element = createElement('c-unit-test', {
          is: UnitTest
        });
        document.body.appendChild(element);
        const div = element.shadowRoot.querySelector('div');
        // Trigger unit status input change
        const inputElement = element.shadowRoot.querySelector('lightning-input');
        inputElement.value = 7;
        inputElement.dispatchEvent(new CustomEvent('change'));
        return Promise.resolve().then(() => {
          expect(div.textContent).toBe('Unit 7 alive!');
        });
      });
     
  3. 保存文件并运行测试以查看失败的消息。
    错误信息。您可以看到只运行了一个测试,而其他两个则被跳过。

让我们看一下正在测试的内容:

  • 前几行应该很熟悉。您正在将UnitTest添加到中document.body,然后创建对的引用div
  • inputElement参照雷电输入字段来设置常数。
  • 接下来,将该输入字段的值设置为7。
  • 然后,我们使用事件类型为“ change”的事件dispatchEvent来触发事件CustomEvent
  • Promise是熟悉的,只有改变改变的输入字段的值。

让我们更新代码以使其通过。为此,将添加lightning-input到HTML文件,并将handleChange方法添加到JavaScript控制器。

  1. 打开unitTest.html
  2. 将以下代码添加到中的lightning-card和之前div
      <lightning-input
        label="Unit Number"
        value={unitNumber}
        onchange={handleChange} >
      </lightning-input>
     
  3. 保存文件。
  4. 打开unitTest.js
  5. @api unitNumber语句之后添加以下代码:
      handleChange(event) {
        this.unitNumber = event.target.value;
      }
     
  6. 保存文件并运行测试。
  7. 由于添加了input元素和JavaScript处理程序,因此测试通过。

测试生命周期挂钩

闪电Web组件具有由框架管理的生命周期。该框架创建组件,在DOM中添加和删除它们,并在组件状态改变时呈现DOM更新。有几种与生命周期交互的方法。 

connectedCallback()组件插入DOM时会触发生命周期挂钩。在disconnectedCallback()当组件从DOM除去生命周期钩闪光。这些挂钩的一种用途是注册和注销事件监听器。 

看一下 lwc-recipes示例存储库中lmsSubscriberWebComponent中的代码,这 是一个很好的示例。

接下来,我们看一下为有线服务编写Jest测试。