今回はJestを使ってLightning Web Component(以降lwc)のテストコードを実装する方法について紹介します。
Jest とは
lwc以外、Node.js、Angular.js などをテストすることができます。
テスト結果にコードカバレッジを含めることが可能です。
テストする際、サーバー通信は発生しないため、サーバー通信結果のモックを作る必要があります。
環境準備
Jestの導入するには下記が必要です:
- Node.js と npm インストール(オフィシャル) インストール(日本語)
- Salesforce CLI (以降:sfdx-cli)オフィシャルサイト
1 と 2 は別々でインストールすることが可能ですが、npmなどのパッケージ管理ソフトを使ってインストールすることをおすすめします。
npm で sfdx-cli をインストールする場合、npm インストール後にnpm install -g sfdx-cli
を実行します。
sfdx-cliインストール後、jestのインストールに移ります。
Jestをインストール及び設定
Jestはプロジェクト単位でインストールするため、事前にローカルに下記を用意します。
- Salesforceプロジェクト
- Salesforce DX プロジェクトの作成 (コマンドライン)
- Salesforce DX プロジェクトの作成 (Visual Studio Code)
- Visual Studio Code導入はまだの方はリンク先サイト左側の「インストール」をクリック
コマンドラインで現在のディレクトリを前述Salesforceプロジェクトのルートディレクトリにした後、下記順番のコマンドを実行してJestをインストールします。
npm init -y
- Salesforceプロジェクトにnpmの初期化を行います。正常終了の場合、プロジェクトのルートディレクトリにpackage.jsonが作成されます。
-y
を外して実行した場合、package.json作成時に一部項目の入力が要求されます 。
sfdx force:lightning:lwc:test:setup
- Jest及び関連モジュールをインストールします。
Jestインストール完了後、package.jsonを開いて、scripts
ブロックが空白の場合、下記のように追記します(...
除く)。
{ ... "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", ... }, ... }
テスト対象 lwc 作成
サンプルとして、下記取引先と取引先責任者を出力する lwc を用意します。
<template> <div class="content"> <template if:true={accountRecord}> <div class="name"> <div>取引先名:</div> <div>{name}</div> </div> </template> </div> <lightning-card title="取引先責任者" icon-name="custom:custom107"> <template if:true={contacts}> <template for:each={contacts} for:item="contact"> <p key={contact.Id}>{contact.LastName} {contact.FirstName}</p> </template> </template> </lightning-card> </template>
import { LightningElement, wire, api, track } from 'lwc'; import { getRecord } from 'lightning/uiRecordApi'; import getContacts from '@salesforce/apex/MyAccountLwcCompController.getContacts'; import ACCOUNT_NAME from '@salesforce/schema/Account.Name'; export default class MyAccountLwcComp extends LightningElement { // 取引先Id @api recordId; // 取引先レコード accountRecord; // 取引先責任者 contacts; // 取引先名 name = ''; @wire(getRecord, { recordId: '$recordId', fields:[ACCOUNT_NAME] }) wiredRecord({ data }) { if (data) { this.accountRecord = data; this.name = data.fields.Name.value; } } @wire(getContacts, { accountId: '$recordId' }) wiredContacts({ data }) { if (data) { this.contacts = data; } } }
public with sharing class MyLwcComp1Controller { @AuraEnabled(cacheable=true) public static List<Contact> getContacts(Id accountId){ return [ SELECT Id, Name FROM Contact WHERE AccountId = :accountId ]; } }
テストコード作成
Jestのテストコードを作成するにはまず、下記コマンドを実行してテスト用のjavascriptファイルを作成します。
sfdx force:lightning:lwc:test:create -f force-app/main/default/lwc/myButton/myAccountLwcComp.js
-f
のパスはテストコードのパスではなく、テスト対象lwcのjavascriptファイルのパスであることに注意してください。
コマンド実行後、下記のものが作成されます:
- lwcバンドルのフォルダ内に
__tests__
フォルダが作成されます。- 今回の例だと:
force-app/main/default/lwc/myAccountLwcComp/__tests__
になります。
- 今回の例だと:
__tests__
フォルダ内に myAccountLwcComp.test.js ファイル作成されます。
sfdx-cliは自動的にやってくれますが、念の為プロジェクトのルートにある.forceignore
ファイルを開いて、__tests__
が除外対象であることを確認します。具体的には下記のような構文です:
# LWC Jest **/__tests__/**
上記の記述で__tests__
フォルダを force:source:status
のチェック対象から外すことができます。Scratch Orgを使わない場合は気にする必要がありません。
myAccountLwcComp.test.js を開いて、下記のようにテストコードを入れます。
import { createElement } from 'lwc'; import MyAccountLwcComp from 'c/myAccountLwcComp'; // テスト対象lwcインポート // ワイヤーアダプターインポート // 標準Lightning Data Service import { registerLdsTestWireAdapter } from '@salesforce/sfdx-lwc-jest'; // Apex クラス import { registerApexTestWireAdapter } from '@salesforce/sfdx-lwc-jest'; // lwcに使われたワイヤーサービスをインポート import { getRecord } from 'lightning/uiRecordApi'; import getContacts from '@salesforce/apex/MyAccountLwcCompController.getContacts'; // テスト用ワイヤーアダプター登録 const getRecordWireAdapter = registerLdsTestWireAdapter(getRecord); const getContactsWireAdapter = registerApexTestWireAdapter(getContacts); // ワイヤーアダプターのテスト用レスポンスをインポート const mockGetRecord = require('./data/getRecord.json'); const mockGetContacts = require('./data/getContacts.json'); // describeはテストスイートに相当します // describe内複数のit(), test() をぶら下がることが可能 // 1番目の引数はテストスイート名で、コマンドでファイル作成する場合 // 自動的に設定されます。 describe('c-my-account-lwc-comp', () => { afterEach(() => { // afterEachはdescribeブロック内 // 各it()、もしくはtest()が実行完了した後に実行されます while (document.body.firstChild) { // it()、test() 開始時に作成したlwcのDOMをクリアする document.body.removeChild(document.body.firstChild); } }); // it(), test() はテストケースに相当します。it は test の略称なので // it() を使うか test() を使うかは開発者の好みでいいです。 // なお、コマンドでファイル作成する場合、デフォルトはit() // 1番目の引数はテストケース概要で、テスト目的がわかる情報を記入します。 it('testGetRecord', () => { // lwcのDOMを作成する const element = createElement('c-my-account-lwc-comp', { is: MyAccountLwcComp }); document.body.appendChild(element); //lwcのDOMをdocumentに入れる //@wire のダミーサーバー通信結果生成 getRecordWireAdapter.emit(mockGetRecord); return Promise.resolve().then(() => { const content = element.shadowRoot.querySelector('.content'); const nameField = mockGetRecord.fields.Name.value; //テンプレートリレラル(` backtick) expect(content.textContent).toBe(`取引先名:${nameField}`) }); }); test('testGetContacts', () => { const element = createElement('c-my-account-lwc-comp', { is: MyAccountLwcComp }); document.body.appendChild(element); //@wire のダミーサーバー通信結果生成 getContactsWireAdapter.emit(mockGetContacts); return Promise.resolve().then(() => { const contactElements = element.shadowRoot.querySelectorAll('p'); expect(contactElements.length).toBe( mockGetContacts.length ); }); }); });
テスト対象コードに @wired
サービスも使用していますので、それもテスト対象になります。
Jestのテストは実際サーバー通信発生しないため、サーバー通信が発生する処理をテストする際、LDS、Apexメソッドからのレスポンスは別途用意する必要があります。
まず、__tests__
フォルダ内にdata
フォルダを作成し、下記.json
ファイルを作成します。
- getRecord.json
- getContacts.json
各ファイルの中身は下記の通りです。
{ "fields": { "Name": { "value": "デモ取引先" } } }
[ { "Id": "1", "LastName": "test", "FirstName": "contact A1" }, { "Id": "2", "LastName": "test", "FirstName": "contact A2" }, { "Id": "3", "LastName": "test", "FirstName": "contact A3" } ]
テスト実施
Jestのテストを実施するには、コマンドラインに現在のディレクトリをプロジェクトのルートディレクトリに移動し、下記コマンドを実行します。
npm run test:unit
コードの変更を保存する度にテストを実施するには下記コマンドを実行します。
npm run test:unit:watch
デバッグモードでテストを実行するには下記コマンドを使います。
npm run test:unit:debug
テストを実行し、終わったらカバー率を出力するには下記コマンドを使います。
npm run test:unit:coverage
上記コマンドは全部 package.json の scripts
ブロックに定義したものです。
そのまま実行するとプロジェクト内すべてのテストコードが実行されます。
個別テストを実行したい場合は、下記のようにコマンドの後に.test.js
のファイル名を追加しましょう。
npm run test:unit myAccountLwcComp.test.js
テスト結果確認
正常終了
- Test Suites
- テストスイート(describeに該当します)
- Tests
- テストケース( it()、test() に該当します)
- Snapshots
- UI(スナップショット)テスト。
- Time
- テスト実行時間(秒)
コードカバー率
カバレッジテーブル部分のヘッダーについて:
- File
- テスト対象 javascript ファイル
- Stmts
- 実行された命令のカバレッジ
- Branch
- if、caseなど分岐のカバレッジ
- Funcs
- 関数呼び出しのカバレッジ
- Lines
- テスト対象ファイル内実行可能の行のカバレッジ
- Uncovered Line
- テストでカバーできなかった行
コマンドラインの出力はもちろん、他にはプロジェクトのルートディレクトリにcoverage
のフォルダが自動的に作成されます。coverage/lcov-report/index.html
を実行すると下記のようなウェブページ版テスト結果が見られます。
assert 失敗
- Expected
- 期待結果
- Received
- 実際結果
後書き
ここまでご覧いただきありがとうございます。
lwcのテストは Apex のテストクラスのように強制ではないので、なくてもリリースに支障はありません。
ただし、テストコードがあれば、下記のようなメリットがあります:
- CI/CDパイプラインで自動テストすることが可能です。
- 画面操作しなくてもテスト実施でき、しかも速いので、テスト駆動開発・コードメンテナンスには最適です。
- テストコードは Selenium Webdriver のUIテストにも使えます。
最終的にlwcの品質向上につながるので、初期設定は諸々ありますが、開発・テスト時に楽にするためにもぜひ習得したいものです。
参考資料
- Jest 公式日本語サイト
- lwc レシピー
- サンプル lwc の集まりです。
- テスト駆動開発
- 本編には触れていませんが、Jestを活用すればlwcも手軽くテスト駆動開発ができます。
- 関連トレイルヘッド (英語)
- Salesforce CLI コマンドリファレンス (英語)
- Test Lightning Web Components (英語)