Articles

Web コンポーネントのテスト ワークフロー

他のユーザーが使用するために何かを出荷するときは常に、安全で安定したコードを提供する責任を負います。 これに対処する 1 つの方法は、コードをテストすることです。どんなに小さくても、どんなにシンプルなプロジェクトでも、理想的には常にテストがあるべきです。そう、現実は厳しく、そうならない場合も多々あると思いますが、常にテストを持つよう努力すべきです。

免責事項 1188 では、シンプル版の入力要素を作成するつもりです。 このチュートリアルの終わりには、open-wc テスト ツールを実践するためのスキルと知識を得ることができ、強固でアクセスしやすく、十分にテストされた入力コンポーネントを構築することができます。

警告

これは、Web コンポーネントを扱う際のいくつかの落とし穴と難しいケースを示す、深いチュートリアルです。 これは間違いなく上級者向けです。 LitElement と JSDoc Type についての基本的な知識を持っている必要があります。 Mocha、Chai BDD、Karma が何であるかを知っていることも、少しは役に立つかもしれません。

私たちは、これをより簡単に消化できるバージョンで投稿することを考えています。

コンソールで実行する

$ npm init @open-wc# Results in this flow✔ What would you like to do today? › Scaffold a new project✔ What would you like to scaffold? › Web Component# Select with space! "Testing" => just enter will move one with no selection✔ What would you like to add? › Testing✔ Would you like to scaffold examples files for? › Testing✔ What is the tag name of your application/web component? … a11y-input✔ Do you want to write this file structure to disk? › YesWriting..... done✔ Do you want to install dependencies? › No

詳細については、https://open-wc.org/testing/を参照してください。

Delete src/A11yInput.js

Modify src/a11y-input.js to:

and test/a11y-input.test.js to:

これまでのテストは、1 つの機能 (label 属性) と 1 つのアサーション (expect) から構成されています。 私たちは karma と chai の BDD 構文を使用しているので、テストのセット (it) を、それらが関連する機能または API (describe) の下にグループ化しています。

実行して、すべてが正しく動作するかどうかを見てみましょう。 npm run test.

Awesome – 期待どおり (🥁)、失敗したテストがあります 🙂

Watch モードに切り替えて、コードを変更するたびにテストを継続的に実行してみましょう。

npm run test:watch

以下のコードは、上記のビデオで src/a11y-input.js:

static get properties() { return { label: { type: String }, };}constructor() { super(); this.label = '';}

ここまではいいですか。 まだ、私たちと一緒ですか? 素晴らしい! 少しゲームをアップグレードしましょう。

Shadow DOM のテストを追加する

要素のシャドウ ルートのコンテンツをテストするためにアサーションを追加しましょう。

予想どおり、次のようになります。

では、それを要素に実装しましょう。

render() { return html` <slot name="label"></slot> <slot name="input"></slot> `;}

興味深いことに、テストは緑のはずですが … そうではありません🤔 見てみましょう。 これは、lit-html が動的な部分の位置を記憶するために使用するマーカーで、効率的に更新することができます。 しかし、テストでは、これを扱うのは少し面倒です。

DOM を比較するために innerHTML を使用する場合、単純な文字列の等価性に頼らざるを得ません。 そのような状況では、生成された DOM の空白やコメントなどを正確に一致させる必要があります。 本当に必要なテストは、レンダリングしたい要素がレンダリングされるかどうかだけです。

それでは、試してみましょう💪

// old:expect(el.shadowRoot.innerHTML).to.equal(`...`);// new:expect(el).shadowDom.to.equal(` <slot name="label"></slot> <slot name="input"></slot>`);

Bam 🎉

a11y input ✔ has by default an empty string as a label ✔ has a static shadowDom

shadowDom.to.equal() はどのようにして機能しますか。

  1. シャドウルートの innerHTML を取得する
  2. それを解析する(実際は。
  3. それを正規化します (潜在的に、すべてのタグ/プロパティはそれ自身の行にあります)。
  4. 期待される HTML 文字列を解析し正規化します。 をグループ化し、差分をわかりやすく表示する

より詳しく知りたい場合は、semantic-dom-diff のドキュメントをチェックしてみてください。

“Light” DOM のテスト

Light DOM でもまったく同じことを行うことができます。 (ユーザーまたはデフォルトによって提供される DOM、つまり要素の children).

そして、それを実装してみましょう。

そこで、光と影の DOM をテストしました💪 そして、テストはきれいに実行されました🎉

Note: ライト要素のライフサイクルで DOM API を使用することはアンチパターンですが、a11y を許可することは実際のユースケースかもしれません – とにかく、説明の目的には最適です。

再びスケルトンから始めます。 src/my-app.js

そして、test/my-app.test.js;

でテストを実行すると => 失敗し、src/a11y-input.js
に実装が追加されました。

<a11y-input> <label slot="label"></label> <input slot="input"></a11y-input>

例えば、a11y-input は実際には my-app shadow dom の内部にノードを作成しています。 とんでもない!

幸運なことに、.shadowDom にはもうひとつの強力な手段があります。

expect(el).shadowDom.to.equal(` <h1>My Filter App</h1> <a11y-input></a11y-input>`, { ignoreChildren: });

さらに、以下のプロパティも指定できます:

  • ignoreChildren
  • ignoreTags
  • ignoreAttributes (グローバルまたは特定のタグに対して)

より詳しいことは、 semantic-dom-diff をご覧ください。

スナップショットテスト

多くの大きなドメインツリーがある場合、すべての手動で書かれた期待値を書いたり維持することは大変なことだと思います。

// fromexpect(el).shadowDom.to.equal(` <slot name="label"></slot> <slot name="input"></slot>`);// toexpect(el).shadowDom.to.equalSnapshot();

では、コードを変更すると、

npm run test を実行すると、ファイル __snapshots__/a11y input.md が作成されて、次のような内容で満たされます。

ファイル __snapshots__/a11y input.md がすでに存在する場合、出力と比較され、html 出力が変更された場合、エラーが発生するようになりました。

詳細は semantic-dom-diff.

Dom ツリーの比較については、もう十分だと思います…
そろそろ変更しましょう🤗

Code coverage

open-wc setup でテストを行う際に得られるもうひとつの有用な測定基準はコード カバレッジです。 コード カバレッジは、コード内のどれだけがテストによってチェックされるかを示す尺度です。 もしテストがカバーしていない行、ステートメント、関数、またはブランチ (例: if/else ステートメント) があれば、カバレッジ スコアに影響します。
npm run test が必要なだけで、次のようになります。 かなりすてきですね!

では、逆に、テストを追加する前に src/a11y-input.js にコードを追加してみましょう。 カスタム要素を介して入力の値に直接アクセスし、その値が ‘cat’ であるときはいつでも、何かを記録したいとします。

これは、大きく異なる結果です

以前よりはるかに低いカバレッジになっています。 すべてのテストが正常に実行されているにもかかわらず、テスト コマンドは失敗さえします。
これは、デフォルトで open-wc の設定により、コード カバレッジのしきい値が 90% に設定されているためです。

カバレッジを改善するには、テストを追加する必要があります。

ブラウザでのデバッグ

watch でテストを実行すると、karma はテストを実行するために永続的なブラウザ環境を設定します。

  • npm run test:watch
  • visit http://localhost:9876/debug.html

このように表示されます。

丸で囲んだ再生ボタンをクリックすると、個別のテストのみを実行することができます。

では、Chrome Dev Tools (F12) を開いて、テスト コードにデバッガーを入れてみましょう。

くっ…この時点より前にエラーが発生した…
このような「致命的」エラーは、テストの失敗ではなく、コンポーネント全体の崩壊の一種なので少し厳しいものです。

set value(newValue) { debugger;

さて、うまくいったので、Chrome コンソールに console.log(this) ここで何が起こったか見てみましょう

<a11y-input> #shadow-root (open)</a11y-input>

ああこれだ – セッターが呼ばれたときにシャドウ領域はまだレンダーされていないんだ。
なので、念のためbefore

set value(newValue) { if (newValue === 'cat') { console.log('We like cats too :)'); } if (this.inputEl) { this.inputEl.value = newValue; }}

ファテルエラーはなくなりました🎉
でも、テストは失敗しました😭

✖ can set/get the input value directly via the custom elementAssertionError: expected '' to equal 'foo'

戦術の変更が必要かも🤔
別のvalueプロパティとして追加して必要時に同期すれば良いのでは。

そして、ようやく再開です 🎉

ok バグを修正しました – カバレッジに戻っていただけますか? ありがとうございます🙏

Back to coverage

この追加テストで、いくつかの進展がありました。 Mac ではコマンド ラインから open coverage/index.html

このように表示されます。

a11y-input.js をクリックすると、どの行がどのくらい実行されたかを行単位で表示します。

では、そのためのテストを追加しましょう

=============================== Coverage summary ===============================Statements : 100% ( 24/24 )Branches : 75% ( 3/4 )Functions : 100% ( 7/7 )Lines : 100% ( 24/24 )================================================================================

これで、ステートメントでは 100% に戻りましたが、ブランチでまだ何かが欠けています
では、その理由を見てみましょう。

このEelse path not takenを意味します。
つまり、関数updateが呼ばれるときは常にchangedPropertiesにプロパティvalueがあります。

同様にlabelがあるので、テストしてみるのもよいアイデアでしょう。 👍

boom 100% 💪 we win 🥇

=============================== Coverage summary ===============================Statements : 100% ( 24/24 )Branches : 100% ( 4/4 )Functions : 100% ( 7/7 )Lines : 100% ( 24/24 )================================================================================

でも待って、上のテストも終わってない – コードはまだ

 // somehow check that console.log was called

テストカバー率100%はどうしてだろうか?

まずはコードカバレッジがどのように機能するかを理解しましょう🤔
コードカバレッジを測定する方法は、instrumentationの形式を適用することで行われます。 簡単に言うと、コードが実行される前に変更され (instrumented)、次のように動作します:

Note: これは説明のための超簡略化したバージョンです。 どのフラグがトリガーされたかに基づいて、統計が作成されます。

つまり、テスト カバレッジ 100% とは、コード内のすべての行が、すべてのテストが終了した後に少なくとも 1 回実行されたことだけを意味します。 それは、すべてをテストしたこと、またはテストが正しいアサーションを行うことを意味しません。

したがって、コード カバレッジがすでに 100% であっても、私たちはまだログ テストを改善するつもりです。

したがって、コード カバレッジは、コードの品質を保証するものではなく、欠けているテストを発見するためのガイダンスやヘルプを提供するだけのツールであると考えるべきです。
open-wc では、スパイやその他の関連タスクのための多くのツールを提供する由緒ある sinon パッケージを推奨しています。

npm i -D sinon

そこで、特定のオブジェクトにスパイを作成し、それがどれくらい頻繁に呼び出されているかをチェックすることができます。 テストは失敗しました:

AssertionError: expected 0 to equal 1

console のようなグローバル オブジェクトを扱うことは副作用があるかもしれないので、専用のログ関数を使用してリファクタリングしましょう。 デバッグしてみると…どうやらupdateは同期していないようです。私の間違った思い込みです🙈思い込みは危険だとよく言いますが、それでも時々陥ります😢.

ではどうすればいいのでしょう?
Let’s create an issue for it https://github.com/Polymer/lit-element/issues/643.

今のところ、プライベートAPIに依存するしかないようです。 🙈
あと、value syncをupdatedに移動して、domレンダリングのたびに実行されるようにする必要がありました。

そして、ロギング用の更新されたテストです。

うわー、これは予想より少し大変でしたが、やりました 💪

SUMMARY:✔ 7 tests completedTOTAL: 7 SUCCESS

Karma フレームワークを使わずにテストを実行

Karma フレームワークは強力かつ機能豊富ですが、時にはテスト手順を縮小したいことがあります。 これまで提案したすべてのものでよかったのは、ベアモジュール指定子を 1 つの例外として、トランスパイルの必要がなく、ブラウザ標準の es モジュールだけを使用することです。

を作成し、クロームで owc-dev-server を介してそれを開くと、完全に動作します。
webpackkarma なしですべてを立ち上げて実行しました – すばらしい🤗

Cross-Browser Thing

これで私たちは、Web コンポーネントを快適に使用できると思います。 すべてのブラウザで実行され、テストされていることを確認したいのです。

それでは、実行してみましょう。

テストに失敗した場合は、失敗した特定のブラウザとともにサマリーに出力されます。

特定のブラウザをデバッグする必要がある場合は、

また、テストするブラウザを調整したい場合は karma.bs.config.js を調整できます。

たとえば、Firefox ESR を追加したい場合。

あるいは、2 つの特定のブラウザだけをテストしたい場合。

注: これは webpack merge strategies replace.

Quick Recap

  • Testing is important for every project.

    Notation for your browser. できる限り多く書くようにしてください。

  • コードカバレッジを高く保つようにしますが、魔法の保証ではないので、常に 100% である必要はないことを忘れないでください。
  • ブラウザで npm run test:watch を使ってデバッグしてください。 レガシー ブラウザの場合は、npm run test:legacy.watch を使用します。

What’s Next?

  • CI でテストを実行します (browserstack と共に完全にうまく動作します)。 automating.

Follow us on Twitter, or follow me on my personal Twitterで私たちのおすすめをご覧ください。
open-wc.org の他のツールや推奨品もぜひチェックしてください。

Pascal と Benny には、フィードバックをいただき、私の走り書きをフォローできるストーリーに変える手助けをしていただきました。