Stencilで、コンポーネントのユニットテスト
Stencilを使用してWebコンポーネントを作る場合に、テストの書き方について整理しました。サンプルコンポーネントModalの表示・非表示を制御するコンポーネントにテストを追加する方法を説明しています。@Methodと@Eventをテストする方法についても述べられています。また、スナップショットテストを使用することもできます。Jestを使ったモックについても説明があります。さらに、参考記事のリンクも掲載されているので、Stencilでテストをする際に役立つでしょう。
目次
StencilでWeb Componentを作ることがあるので、ユニットテストの書き方を整理しました。
テストを書くサンプルコンポーネント
モーダルの表示・非表示を制御するコンポーネントにテストを追加する想定です。
import { Component, Host, h, Prop, Method, Element, Event, EventEmitter } from '@stencil/core';
@Component({
tag: 'demo-modal',
styleUrl: 'demo-modal.scss',
shadow: true,
})
export class DemoModal {
@Element() el: HTMLDemoModalElement;
/**
* Modal state.
* If true, the modal will open
*/
@Prop() open = false;
/**
* Toggle modal state
*/
@Method()
public async toggleModal() {
this.open = !this.open;
if (this.open === false) {
this.close.emit();
}
}
/**
* Open the modal
*/
@Method()
public async openModal() {
this.open = true;
}
/**
* Close the modal
*/
@Method()
public async closeModal() {
this.open = false;
this.close.emit();
}
/**
*
*/
@Event() close: EventEmitter;
render() {
const { open } = this;
return (
<Host>
<div class={`modal-row${open ? ' open' : ''}`} onClick={() => this.closeModal()}>
<div class="modal-child" onClick={e => e.stopPropagation()}>
<slot></slot>
</div>
</div>
</Host>
);
}
}
stencil g
コマンドで生成されるテストコードはだいたいこんな感じのはずです。
import { newSpecPage } from '@stencil/core/testing';
import { DemoModal } from '../demo-modal';
describe('demo-modal', () => {
it('renders', async () => {
const page = await newSpecPage({
components: [DemoModal],
html: `<demo-modal></demo-modal>`,
});
expect(page.root).toEqualHtml(`
<demo-modal></demo-modal>
`);
});
});
コンポーネント内のメソッド(@Method)をテストする
まずは@Method
をテストします。ここではopenModal
を試しましょう。
/**
* Modal state.
* If true, the modal will open
*/
@Prop() open = false;
/**
* Open the modal
*/
@Method()
public async openModal() {
this.open = true;
}
メソッドなどをテストする場合は、コンポーネントを通常のクラスとして実行します。
const component = new DemoModal()
@Props
の値はPublicなので、外から変更できます。
component.open = false
同様に@Method
の呼び出しも可能です。
await component.openModal()
「openModal
を呼び出すと、open
プロパティがfalse
からtrue
に変わる」をテストするコードは、こうなります。
import { DemoModal } from '../demo-modal';
describe('#openModal', () => {
it("open props should change to true when the openModal method was called", async () => {
const component = new DemoModal()
component.open = false
await component.openModal()
expect(component.open).toEqual(true)
})
})
@Event
のEmitterをモックしてテストする
次は@Event
のテストです。
/**
* Close the modal
*/
@Method()
public async closeModal() {
this.open = false;
this.close.emit();
}
/**
*
*/
@Event() close: EventEmitter;
@Event
は「不必要に呼び出されていないか」などだけをテストしたいので、Jestでモックしましょう。
const component = new DemoModal()
component.close = {
emit: jest.fn(),
}
あとはこちらも@Method
を呼び出してテストするだけです。
it("open props should change to false when the closeModal method was called", async () => {
const component = new DemoModal()
component.close = {
emit: jest.fn(),
}
await component.closeModal()
expect(component.open).toEqual(false)
})
「2回以上イベントが発火していないか」などをチェックするために、モックした@Event
をテストしましょう。
jest.fn()
を一旦変数に入れておく方が、コードを読んだ時にわかりやすいかなと思います。
it("When the closeModal method called, should called close event at once", async() => {
const component = new DemoModal()
component.open = true
const mockEmitter = jest.fn()
component.close = {
emit: mockEmitter
}
await component.closeModal()
expect(mockEmitter).toBeCalledTimes(1)
})
スナップショットテストをする
「描画されるHTMLが意図せず変わっていないか」のテストに絞る場合は、スナップショットテストも使えます。
it("should match snapshot (open='true')", async () => {
const page = await newSpecPage({
components: [DemoModal],
html: `<demo-modal open="true"></demo-modal>`,
})
expect(page.root).toMatchSnapshot()
})
スナップショットが生成されました。
exports[`demo-modal Rendering test should match snapshot (open='true') 1`] = `
<demo-modal open="true">
<mock:shadow-root>
<div class="modal-row open">
<div class="modal-child">
<slot></slot>
</div>
</div>
</mock:shadow-root>
</demo-modal>
`;