FreshのIslandコンポーネントをテストする
はじめに
FreshのIslandコンポーネントのテストを書けないか試してみたので、公式のサンプルであるCounterコンポーネントを例に方法について紹介します。 追記
前提
このページは以下のバージョンを想定して書かれています。
コード
以下のような形でテストを用意してみたところ、うまくいきました。
それぞれ解説していきます。
code:test/islands/Counter.test.tsx
import Counter from "../../islands/Counter.tsx";
// (1)
import {
afterEach,
beforeAll,
describe,
it,
// (2)
import {
cleanup,
fireEvent,
render,
import vm from "node:vm";
// (3)
beforeAll(() => {
// NOTE: Deno v1.34時点では未実装のため
const isContext = vm.isContext;
vm.isContext = () => false;
const { document } = new JSDOM().window;
globalThis.document = document;
vm.isContext = isContext;
});
// (4)
afterEach(cleanup);
// (5)
describe("Counter", () => {
it("should work", async () => {
const screen = render(<Counter start={10} />);
assert(screen.queryByText("10"));
const plusOne = screen.getByRole("button", { name: "+1" });
const minusOne = screen.getByRole("button", { name: "-1" });
await fireEvent.click(plusOne);
await fireEvent.click(plusOne);
assert(screen.queryByText("12"));
await fireEvent.click(minusOne);
assert(screen.queryByText("11"));
});
});
(1) std/testing/bdd.tsについて
これはDenoの標準ライブラリであるdeno_stdで提供されているモジュールの一つで、describe()やit()などのAPIを使ってテストケースを記述することができます。 std/testing/bdd.tsで書かれたテストケースは、通常通りdeno testコマンドで実行することができます。 code:tsx
import {
cleanup,
fireEvent,
render,
?external=preactの指定が重要な部分で、このパラメータで指定されたパッケージについてはesm.shはImport Specifierの書き換えを行わず、そのまま維持してくれます。 code:import_map.json
{
"imports": {
...
}
}
(3) globalThis.documentをセットアップする
注意点として、jsdomはnode:vmのisContextなどのAPIに依存していますが、Deno v1.34時点ではまだ未実装のため、一時的に置き換えを行っています。 code:typescript
beforeAll(() => {
// NOTE: Deno v1.34時点では未実装のため
const isContext = vm.isContext;
vm.isContext = () => false;
const { document } = new JSDOM().window;
globalThis.document = document;
vm.isContext = isContext;
});
(4) 各テストケースの終了時にcleanup()を呼ぶ
しかし、Denoの場合は自動では呼ばれないので、自前で呼ぶ必要があります。 code:typescript
afterEach(cleanup);
(5) テストコード
以上のセットアップなどを行っておけば、Node.jsの場合と同様にテストが書けそうです。 code:tsx
describe("Counter", () => {
it("should work", async () => {
const screen = render(<Counter start={10} />);
assert(screen.queryByText("10"));
const plusOne = screen.getByRole("button", { name: "+1" });
const minusOne = screen.getByRole("button", { name: "-1" });
await fireEvent.click(plusOne);
await fireEvent.click(plusOne);
assert(screen.queryByText("12"));
await fireEvent.click(minusOne);
assert(screen.queryByText("11"));
});
});
code:shell
$ deno test ./test/islands/Counter.test.tsx
補足
DOMライブラリについて
アサーションについて
試していないのですが、もしかしたら、以下のあたりを使うとexpect()を使って書ける可能性があるかもしれません。
依存管理について
code:import_map.json
{
"imports": {
...
}
}