Skip to main content

Jest テストの記述

学習の目的

この単元を完了すると、次のことができるようになります。

  • 各自の設定を検証するテストを記述する。
  • 失敗するテストを記述して、合格するようコンポーネントを変更する。
  • Jest の基本的なコマンドを挙げる。
  • ライフサイクルフックについて説明する。

Lightning Web コンポーネントの事前準備

Lightning Web コンポーネントをテストするにはまず、テストするコンポーネントが必要です。

Lightning Web コンポーネントの作成

  1. Visual Studio Code で、Ctrl+Shift+P (Windows) または Cmd+Shift+P (macOS) を押して、コマンドパレットを開きます。
  2. 「lightning web」と入力します。
  3. [SFDX: Create Lightning Web Component (SFDX: Lightning Web コンポーネントの作成)] を選択します。
  4. 新しいコンポーネントの名前に「unitTest」と入力します。
  5. Enter キーを押します。
  6. もう一度 Enter キーを押して、デフォルトの force-app/main/default/lwc を受け入れます。

lwc ディレクトリに unitTest ディレクトリが作成され、初期基本ファイルが設定されます。

test-lwc プロジェクトの unitTest ディレクトリ。

基本的なテストの記述

Jest テストは、Aura コンポーネントの Lightning Testing Service に記述する Jasmine テストや Mocha テストとは、記述、保存、実行の方法が異なります。Jest テストはローカルに限定され、Salesforce とは関係なく保存され実行されます。実際、Jest テストを Salesforce 組織にリリースしようとすると、エラーが生じます。Lightning Web コンポーネントの Jest テストは Salesforce 組織にリリースされませんが、コンポーネント自体とともに必ずバージョン管理にコミットします。

__tests__ フォルダー

テストファイルはコンポーネントの他のファイルと切り離しておく必要があります。コンポーネントのバンドルフォルダーの最上位に __tests__ というフォルダーが自動作成されていなければ、作成します。このコンポーネントのテストはすべてこの __tests__ フォルダー内に保存します。

  1. Visual Studio Code で、unitTest ディレクトリを右クリックし、[New Folder (新しいフォルダー)] を選択します。
  2. 「__tests__」と入力します。
  3. Enter キーを押します。

.forceignore の設定

__tests__ フォルダーをバージョン管理にコミットして、テストを他のチームメンバーやシステムと共有します。テストはプロジェクトや継続的インテグレーションプロセスの重要な要素です。Salesforce にリリースされないようにするために、.forceignore ファイルにテストの除外を入力しておきます。

  • プロジェクトの .forceignore ファイルに次の除外が挿入されていることを確認します。挿入されていない場合は、追加してファイルを保存します。
    # LWC configuration files
    **/jsconfig.json
    **/.eslintrc.json
    # LWC Jest
    **/__tests__/**

Jest テストファイルを作成する

最初のテストは簡単です。引数として渡される 2 つの数字を加算することが期待される 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 で、[View (表示)] を選択し、[Terminal (ターミナル)] を選択します。Visual Studio Code にターミナルが開きます。ターミナルはデフォルトで現在のプロジェクトの最上位ディレクトリに設定されます。
  2. ターミナルで、前の単元の次のコマンドを実行します。
    npm run test:unit
  3. このテストは sum 関数がないため失敗します。

この修正方法を見てみましょう。

  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 JavaScript ファイルからエクスポートされた sum 関数をインポートします。
  • 3 行目以降が Jest テストスイートです。describe 関数またはブロックがテストスイートで、2 つの引数を受け入れます。1 つ目はテストする単体の説明で、通常は名詞形です。2 つ目は 1 つ以上のテストを保持するコールバック関数です。明確にするために、describe テストスイートを相互にネストすることも可能です。
  • 4 行目がテストです (ittest の別名です)。この it 関数またはブロックも 2 つの引数を受け入れます。1 つ目は、期待する内容の説明で、通常は動詞で始まります。次は、テストを構築して、テストのアサーションまたは期待を保持するコールバック関数です。
  • 5 行目は expect ステートメントで、sum 関数が 1 と 2 の 2 つの引数を追加して、3 を返すという成功条件をアサートしています。toBe は多様な Jest マッチャーの 1 つです。
    5 行目のすぐ後に、次の行のアサーションをもう 1 つ追加します。
        expect(sum(1, 2)).not.toBeGreaterThan(3);
  • .not.toBeGreaterThan を追加することで、数値が 3 以下になります。あるいは、.not.toBeLessThan(3) を使用した expect ステートメントを追加することも可能です。

ここからは Lightning Web コンポーネントのテストについてです。

Lightning Web コンポーネントの Jest テストでは、外部のコンポーネントやサービスとの連動関係を最小限に抑え、1 つのコンポーネントの動作を切り離してテストする必要があります。

プロセスを繰り返して unitTest テストファイルを作成する

このテストでは、プロパティが設定されており、DOM に追加すると正しいテキストが表示されることを検証します。[SFDX: Create Lightning Web Component (SFDX: Lightning Web コンポーネントの作成)] コマンドの実行時に __tests__ フォルダー内に unitTest.test.js が作成されています。

  1. unitTest.test.js 内のコードを次のコードに置き換えます。
    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!');
      });
    });
  2. ファイルを保存します。
  3. ターミナルでもう一度テストを実行します。
    npm run test:unit
  4. テストに失敗し、次の出力が表示されます。
    Test Suites: 1 failed, 1 passed, 2 total
    Tests:       1 failed, 1 passed, 2 total

テストに合格するようコードを更新する前に、テストコードで何が必要なのかを確認しましょう。

  • 1 行目は新しい内容です。lwc フレームワークから createElement メソッドをインポートします。このメソッドはテストのみで使用できます。
  • 2 行目はコンポーネントの JavaScript コントローラーから UnitTest クラスをインポートします。
  • 4 行目以降が describe テストスイートブロックです。
  • 5 行目は Jest のクリーンアップメソッドです。afterEach() は Jest の設定とクリーンアップのメソッドの 1 つです。各テスト後にそれ自体が存在する describe ブロックで afterEach() が実行されます。この afterEach() メソッドは、テストの最後に DOM をリセットします。テストの実行時、Jest がブラウザーを実行するわけではありません。Jest は jsdom を使用して、ブラウザーの DOM または document のように動作する環境を用意します。各テストファイルが jsdom のインスタンスを 1 つ取得しますが、テストの前後にファイル内の変更がリセットされるわけではありません。テストの出力が他のテストに影響しないように、テストの前後にクリーンアップすることがベストプラクティスです。設定とクリーンアップの他のメソッドも使用できます。「リソース」を参照してください。
  • 12 行目からは it テストブロックです。
  • 13 行目で、インポートした createElement メソッドを使用します。このメソッドはコンポーネントのインスタンスを作成し、element 定数に割り当てます。
  • 16 行目には unitNumber 変数が 5 に設定されていることをアサートする expect があります。これは私たちがテストしている 1 つ目の要件で、最初にこの unitNumber が 5 に設定されています。
  • 18 行目は実際に appendChild メソッドを使用して、elementdocument.body の jsdom のバージョンに追加します。このコールによって Lightning Web コンポーネントが DOM にアタッチされ表示されます。つまり、connectedCallback() と renderedCallback() というライフサイクルフックがコールされるということです (詳細は後述)。
  • 20 行目は querySelector (標準の DOM クエリメソッド) を使用して div タグの DOM を検索します。element.shadowRoot をクエリの親として使用します。これは、シャドウの境界を越えてコンポーネントのシャドウツリーを検査できる、テスト限定の API です。
  • 最後の 21 行目は div タグの textContent を確認して、「Unit 5 alive!」であることをアサートする expect です。これが最後の要件です。テキストが正しいことをアサートしています。

テストに合格するには、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 にチェーニングします。Promise チェーンが完了するまで待機して、Jest がテストを終了します。拒否状態で Promise が終了すると、Jest テストに失敗します。

  1. unitTest.test.js を開きます。
  2. 前回のテストの後にこの 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 alive」であるべきとアサートしているのに、「Unit 5 alive!」のままになっています。この変更を確認するには、解決された Promise を返して、変更されるまで待機する必要があります。

  1. // Verify display unit status の後にある、失敗の原因となった expect ステートメントを次のコードに置換します。
        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. このテストは合格します。

順調です。2 つのテストスイートで 3 つのテストに成功したことになります。次に 4 つ目のテスト、を追加して、入力項目が更新されたときに単体の状況が更新されることを確認できるようにします。この場合は、入力項目に変更イベントを使用します。

  1. unitTest.test.js がまだ開いていない場合は開きます。
  2. 追加した前回のテストの後に行を追加して、この 3 つ目のテストをスイートに追加します。
      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. ファイルを保存してテストを実行し、失敗メッセージを確認します。
    エラーメッセージ。 テストが 1 つだけ実行され、他の 2 つはスキップされたことがわかります。

テストされた内容を見てみましょう。

  • 最初の数行は以前と同じです。UnitTest を document.body に追加して、div への参照を作成しています。
  • inputElement 定数に lightning-input 項目への参照が設定されています。
  • 続いて、その入力項目の値が 7 に設定されています。
  • そして、dispatchEvent を使用して、「change」というイベント種別を設定した 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. 入力要素と JavaScript ハンドラーが追加されたため、テストに合格します。

ライフサイクルフックのテスト

Lightning Web コンポーネントには、フレームワークで管理されるライフサイクルがあります。このフレームワークは、コンポーネントを作成して DOM に追加または DOM から削除したり、コンポーネントの状態の変更時に DOM の更新を表示したりします。ライフサイクルを操作するためのメソッドがいくつか存在します。

connectedCallback() ライフサイクルフックは、コンポーネントが DOM に挿入されたときに起動します。disconnectedCallback() ライフサイクルフックは、コンポーネントが DOM から削除されたときに起動します。これらのフックの用途の 1 つは、イベントリスナーを登録または登録解除することです。

適切な例として、lwc-recipes サンプルリポジトリの lmsSubscriberWebComponent のコードを参照してください。

次は、ワイヤーサービスの Jest テストの記述について見ていきます。

リソース

無料で学習を続けましょう!
続けるにはアカウントにサインアップしてください。
サインアップすると次のような機能が利用できるようになります。
  • 各自のキャリア目標に合わせてパーソナライズされたおすすめが表示される
  • ハンズオン Challenge やテストでスキルを練習できる
  • 進捗状況を追跡して上司と共有できる
  • メンターやキャリアチャンスと繋がることができる