Jest + jsdomでCanvas要素を単体テストする

はじめに

この記事は、Jest(jsdom)でContext2Dインターフェイスを利用するCanvas要素の単体テスト方法を記録、共有するためのものです。

想定する読者

この記事は、以下の読者を想定して書かれています。

  • JavaScriptの開発経験がある
  • Jestを使ったことがある

JavaScriptの解説やJestのインストールガイドはこの記事には含まれません。

想定する環境

この記事は、以下の環境を想定して書かれています。記事を読む前に、お手元の環境をご確認ください。

$ node --version
v16.3.0
$ npm --version
7.15.1

また、この記事で扱うパッケージは以下の通りです。

▼package.json

"devDependencies": {
  "canvas": "^2.8.0",
  "jest": "^27.0.4",
  "pixi.js-legacy": "^6.0.4",
}

WebGLの単体テストは想定していません。WebGLコンテンツをテストしたい場合、Puppeteerの導入を検討してください。

参考記事 : JestでPuppeteerを動かしてCIで実行する

先に結論だけ

GitHub - MasatoMakino/minimal-test-environment-jest-pixijs: Minimal example for unit testing pixi.js with jest
https://github.com

JestでCanvas要素をテストする最小限のリポジトリを作りました。

JestとCanvas

Jestは、ブラウザ環境を想定したテストのためにjsdomを内包しています。jsdomとはDOMをJavaScriptで実装したものです。Pure JavaScriptで実装されているため、node.js上で動作します。jsdomを通じて、ユーザーはテストに必要なDOMを組み立てて操作します。

jsdomのCanvasサポート

jsdomはテストやスクレイピングを目的としています。そのためjsdom単体ではCanvas要素をサポートしていません。Canvas要素をDOMツリーに追加しようとすると、jsdomはdiv要素を代わりに挿入し、処理を簡略化します。

jsdomでCanvas要素をサポートする場合、node-canvasを追加します。

jsdom README : Canvas support

jsdom includes support for using the canvas package to extend any canvas elements with the canvas API. To make this work, you need to include canvas as a dependency in your project, as a peer of jsdom. jsdomには、canvasパッケージを使用して任意の canvas 要素をcanvas APIで拡張するためのサポートが含まれています。これを動作させるには、jsdom のピアとして、プロジェクトに canvas を依存関係として含める必要があります。

jsdomはnode-canvasがパッケージ内にインストールされていることを自動で認識します。ユーザーがなにか設定する必要はありません。node-canvasがインストールされている場合、JestはCanvasに関わる処理をnode-canvasに渡します。

Canvasのテスト

それでは実際にCanvasをテストしてみます。今回はCanvas描画ライブラリとしてPixiJSの2Dコンテキスト対応版pixi.js-legacyを使います。

モジュールのインストール

プロジェクトにJestとnode-canvas、pixi.js-legacyをインストールします。

npm install --save-dev jest canvas pixi.js-legacy
npm v7以降の場合

npm v7から、peerDependenciesに指定されたモジュールが自動的にインストールされるようになりました。jsdomはpeerDependenciesにnode-canvasを指定しています。そのため、node-canvasを個別にインストールする必要はありません。

テストの作成

テストファイルを作成します。

▼./__test__/Test.spec.ts

jest.spyOn(console, "error").mockImplementation()

describe("Test", () => {
    const PIXI = require("pixi.js-legacy");
    jest.spyOn(console, "log").mockImplementation((x) => x);

    const app = new PIXI.Application({
        width: 640,
        height: 480,
        forceCanvas: true,
    });
    document.body.appendChild(app.view);

    test("app should be exist", () => {
        expect(app).toBeTruthy();
    })
})

テスト環境を指定して実行

Jestの設定ファイルを作成し、環境にjsdomを指定します。

▼jest.config.js

module.exports = {
  testEnvironment: "jsdom",
};

もしくは、jestコマンドに--env=jsdomオプションを追加します。

▼package.json

"scripts": {
  "test": "jest --env=jsdom"
}

テスト結果

> minimal-test-environment-jest-pixijs@0.1.0 test
> jest --env=jsdom

 PASS  __test__/Test.spec.js
  Test
    ✓ app should be exist (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.393 s
Ran all test suites.

プロセスは終了コード 0 で終了しました

テストがパスすれば成功です。

GitHub Actions

node-canvasはGitHub Actions上でも動作します。ubuntu-latest上でaptコマンドを使ってパッケージを追加するなどの作業は必要ありません。

starter-workflows/node.js.yml at main · actions/starter-workflows
https://github.com

たとえばnode.js用のテンプレートを使えば、そのままテストがGithub Actions上で動作します。

以上、ありがとうございました。