jestのチュートリアルをやってみる

はじめに

JavaScriptユニットテストフレームワークでよく名前を聞くのが、jestですよね。

筆者はまだjestでユニットテストを書いたことが無いですが、ここ最近jest触ってみたいなと思っていたので、チュートリアルをやりたいと思います。

以下の公式チュートリアルで手を動かしていきます!

jestjs.io

始めましょう

インストール

$  npm install -D jest

チュートリアルのソースはnodejsのソースぽいな。requireを使う。 import形式だとまた違うのかな。

//// sum.js
function sum(a, b) {
  return a + b;
}

//// sub.test.js
const sum = require('./sum');

// test, expectはrequireしなくても使える
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

//// packge.json
"scripts" : {
   "test" : "jest"
}
$ npm run test

> jest_sample@1.0.0 test
> jest

 PASS  ./sum.test.js
  ✓ adds 1 + 2 to equal 3 (4 ms)

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

おー、テストが実行されましたね。簡単なもんだな。 これで、初jestテストの実績できたわ。

toBe()はマッチャー、他のマッチャーは以下参照。

jestjs.io

コマンドラインからの実行

  • グローバルインストールしたら、jestコマンドでテスト実行できる
// なんかエラーになったな、何指定していいかよくわかってない。
// configファイルは必須なのかな。
$  jest ./jest_sample/
Error: Could not find a config file based on provided

エラーになったが、一旦良しとしよう。

Jest CLIオプションは以下 jestjs.io

追加設定

ここで、設定ファイルを作っていくのね。

基本の設定ファイルを生成する

  • jest --init で設定ファイルを生成
  • 結構色々聞かれた。
  • configファイルにTSを使うか、実行環境はnodeかとか。
    • よくわかってないが。基本はデフォルト値にした
jest --init
The following questions will help Jest to create a suitable configuration for your project

✔ Would you like to use Typescript for the configuration file? … no
✔ Choose the test environment that will be used for testing › node
✔ Do you want Jest to add coverage reports? … no
✔ Which provider should be used to instrument code for coverage? › v8
✔ Automatically clear mock calls, instances and results before every test? … no

オプションなのか、以下の説明があるが、必ずしも使う必要は無いのか。。

  • Babelを使用する
  • webpackを使用する
  • Parcelを使用する
  • TypeScriptを使用する
    • TypeScriptをBabel経由で使用する
    • TypeScriptをts-jest経由で使用する

ちょっと、jestと上記の関係がわからんな。上記と一緒に使うこともできるよ、くらいか?

jestはテスティングフレームワークなので、babelとかでES5に変換してテストするとか?、テストランナーを他のツールから呼べるようにして、そのときにコードのトランスパイルが必要ならよろしくやるよ、ってことなのかな。

Matcherを使用する

Macherの完全なリファレンスはこちら

jestjs.io

一般的なマッチャー

test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});
  • expect(2 + 2) は"expectation"オブジェクトを返す。
  • toBe(4)の部分がマッチャー
  • Jestが実行されると、失敗したマッチャーをすべて追跡し、エラーメッセージを表示することができる

わざとFailさせてみた。

$ npm run test

> jest_sample@1.0.0 test
> jest

 FAIL  ./sum.test.js
  ✕ adds 1 + 2 to equal 3 (12 ms)

  ● adds 1 + 2 to equal 3

    expect(received).toBe(expected) // Object.is equality

    Expected: 4
    Received: 3

      3 |
      4 | test('adds 1 + 2 to equal 3', ()=>{
    > 5 |     expect(sum(1, 2)).toBe(4);
        |                       ^
      6 | })

      at Object.<anonymous> (sum.test.js:5:23)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.019 s, estimated 2 s
  • toBeObject.isを使用して厳密な等価性をテストする
  • toEqual は オブジェクトの値を確認する
    • deep equality らしいので、再帰的に確認するのかな。
  • notで反対のテスト可能。
    • expect(a + b).not.toBe(0)
  • Object.isの比較は参照比較するぽい。Object.is([],[]) => false Object.is() - JavaScript | MDN

jestではexpectの引数はreceivedって読んでるんだね。actualjUnitの文化なのかな。

真偽値(およびそれらしく思える値)

  • undefined,null,falseを区別したい場合と、一緒として扱いたい場合がある
  • それに対応するマッチャーがある
    • toBeNull: nullのみに一致
    • toBeUndefined : undefinedのみに一致
    • toBeDefined : toBeUndefinedの反対。(undefinedじゃなければ一致)
    • toBeTruthy : ifステートメントが真であるもとみなすものに一致
    • toBeFalsy : ifステートが偽であるとみなすものに一致

数値

  • toBeGreaterThan(x) : より大きい
  • toBeGreaterThanOrEqual(x): 以上
  • toBeLessThan(x): より小さい
  • `toBeLessThanOrEqual(x): 以下
  • toBe(x): 等しい
  • toEqual(x): 等しい

  • toBeCloseTo: 丸め誤差に依存しないように

文字列

  • toMatch: 正規表現でマッチするかを確認できる

配列と反復可能なオブジェクト

  • toContain(): 配列や反復可能なオブジェクトに特定のアイテムが含まれているかどうかを確認できる

例外

  • toThrow : ある関数が呼び出し時に例外を投げることをテストする
    • 例外をスローする関数は、ラッピング関数内で呼び出される必要がある

非同期コードのテスト

  • JSでは非同期コードをよく書く。
  • jestはテスト対象コードがいつ完了したかを別のテストに進む前に知る必要がある
  • いくつか方法がある

コールバック

  • fetchData(callback)のように、処理が終わったらcallback関数を呼び出す場合
  • jestのテストはデフォルトでは一度最後まで実行したら完了
    • callback関数の中でアサートしても呼ばれない
      • テストがpassしたことになってしまう。
  • テスト関数にdoneが引数として渡されるので、expectの後にdone()を呼ぶ
    • 引数にdoneを書いた時点でテストの挙動が変わるな
test('the data is peanut butter', done => {
  function callback(data) {
    try {
      expect(data).toBe('peanut butter');
      done();
    } catch (error) {
      done(error);
    }
  }

  fetchData(callback);
});

Promises

  • promiseを使用するコードなら、非同期テストをもっと簡単に処理できる
    • むしろ、これが本筋というか、あるべきか?
    • fetchData関数がpromiseを返すようにする
    • テスト関数の中でも、promiseを返す。
test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});
  • promiseがrejectされることを期待するケース
    • .catchを利用する
    • 想定した数のアサーションが呼ばれたことを確認するためexpect.assertionsを必ず呼ぶ
      • これ忘れそー
    • rejectの引数にerrorメッセージを入力できる
test('the fetch fails with an error', () => {
  expect.assertions(1); // expectが1回呼ばれたことの確認? でもこの行なくてもエラーにならんな
  return fetchData().catch(e => expect(e).toMatch('error'));
});

expect.assertions(1)がよくわからん。この行なくてもエラーにならんぞ。でも、assertions(2)とかにするとエラーになるのか。だから、この行なくてもfailはしないけど、assertions(1)にして、assertがされなかったらエラーになるので、ちゃんとassertはされていることは確認が必要、ということね。

.resolves / .rejects

  • expect宣言で、.resolvesマッチャを使うこともできる
    • テストメソッドからreturnするのは必須
  • resovle, rejectじゃなくて、resolves, rejectsで複数形なのが間違えそう
test('the data is peanut butter', () =>{
  return expect(fetchDataPs()).resolves.toBe('peanut butter');
  // returnは必須
})

test('the fetch fails with an error', () => {
  return expect(fetchDataPsError()).rejects.toMatch('error');
})

Async/Await

  • asyncとawaitをテストで使用できる
    • testに渡すアロー関数前にasyncキーワードを記述するだけ
      • 圧倒的に、わかりやすい気がする。
  • .resolves、.rejectsと組み合わせて使える
    • returnでpromiseを返していたのをawaitキーワードつけて呼び出す
      • うーむ。単純な例だとasyncを書く分、簡潔って感じではないな
test("the data is peanut butter (async/await ver)", async () => {
  const data = await fetchDataPs();
  expect(data).toBe("peanut butter");
});

test('the fetch fails with an error (async/await ver)', async ()=> {
  expect.assertions(1);
  try {
    await fetchDataPsError()
  } catch (e) {
    expect(e).toMatch('error')
  }
})

// resolveと併用
test("the data is peanut butter, (async/await and resove)", async () => {
  await expect(fetchDataPs()).resolves.toBe("peanut butter");
});

セットアップと破棄

  • junitでいうところの、@beforeEachみたいな機能
    • 共通的なセットアップ作業をまとめれる

テストごとにセットアップ作業を繰り返す実行する

  • beforeEach関数、afterEach関数を使用する
  • 非同期コードでセットアップする場合はpromiseを返すかdoneパラメータを使う

ワンタイムセットアップ

  • ファイルの先頭/最後で1回だけ実行されるセットアップ
  • beforeAll, afterAll

スコープ

  • describeを使って、複数のテストをグループ化できる。
  • describeの中で、beforeEach, afterEachを使うと、グループ内だけに適用される
    • describeの外側で定義されているbeforeEach,afterEachはdescribe内のtestにも有効っぽい
      • describeの外側のbeforeEachとかの方が、describe内よりも先に実行される
  • describeの中で、beforeAll, afterAllも使える

describeブロックとtestブロックの実行順序

  • describeハンドラを実際の全てのテストを実行する前に実行する
    • describeが全て実行された後に、testが実行されるということ

一般的なアドバイス

  • テストが失敗した場合、テストが単体で実行した場合にも失敗するかどうかを確認する
    • testtest.onlyに変更するとそのテストだけを実行できる

モック関数

  • モック関数でできること
    • コード間の繋がりをテストできる
    • 関数がもつ実際の実装を除去できる
    • 関数の呼び出しをキャプチャできる
    • new によるコンストラクタ関数のインスタンス化をキャプチャできる
  • 関数をモックする方法は2つある
    • テストコードの中でモック関数を作成する
    • manual mockを作成してモジュールの依存性を上書きする

モック関数を利用する

  • モックコールバック関数の生成
    • mockCallback = jest.fn(x => 42 + x)
  • 呼ばれた回数
    • mockCallback.mock.calls.length
  • 引数
    • mockCallback.mock.calls[0][0] は 1回目の呼び出しの第一引数
  • 返り値
    • mockCallback.mock.results[0].valueは1回目の呼び出しの返り値

.mockプロパティ

  • mock関数の生成
    • const myMock = jest.fn()
      • 何もしない関数ということかな
  • すべてのモック関数には.mockプロパティがあり、モック関数呼び出し時のデータと、関数の返り値が記録されている
  • thisの値も記録されている

一個上で列挙したプロパティか。

モックの戻り値

  • myMock.mockReturnValueOnce(1)
    • myMock()の呼び出しで、定義した値が変える。
  • 継続渡しスタイルで書ける
    • `myMock.mockReturnValueOnce(10).mockReturnValueOnce('x')

モジュールのモック

  • APIのテストをAPIにアクセスせずにテストする
import axios from 'axios'
jest.mock('axios') // これでモック化される

axios.get.mockResoledValue(xxx) // これでgetを呼ばれたときの返り値を定義できる

部分的なモック

  • モジュールを部分的にモックすることが可能、残りのぶ部は実際の実装そのまま
  • junitで言うところのspy

ESMのサンプルコードになっているがローカルで動かない。。

stack overflowの記事と以下の公式ドキュメント見てみる。

jestjs.io

結局、部分的なモックはよくわからんかったな。エラーになるし。。

モックの実装

  • 指定された値を返すという能力を超えて、完全に実装をモック化する
  • jest.fnmockImplementationOnceメソッドを使う
    • const myMockFn = jest.fn(cb => cb(null, true))
      • myMockFnという関数をcb => cb(null, true)という実装でモック化するということか?
  • mockImplementationOnceメソッドは他のモジュールによって作成されたモック関数のデフォルトの実装を定義したいときに便利
jest.mock('../foo'); // これでrequire('../foo')はモック化される
const foo = require('../foo'); 

foo.mockImplementation(() => 42);
foo() // => 42
  • mockImplementationOnce
    • 関数への複数回への呼び出しで異なる結果を得るようにしたい場合に利用
test(" test multi mockImplementationOnce", () => {
    const myMockFn = jest
        .fn()
        .mockImplementationOnce(cb => cb(null, true))
        .mockImplementationOnce(cb => cb(null, false));

    myMockFn((erro, val) => expect(val).toBe(true))
    myMockFn((erro, val) => expect(val).toBe(false))

})
  • モック関数がmockImplementationOnceによって定義された実装をすべて使いきった場合
    • jest.fnのデフォルトの実装を実行する
test(" test default impl", () => {
    const myMockFn = jest
        .fn(()=> 'default')
        .mockImplementationOnce(() => 'first call')
        .mockImplementationOnce(() => 'second call');

    expect(myMockFn()).toBe('first call');
    expect(myMockFn()).toBe('second call');
    expect(myMockFn()).toBe('default');
});
  • .mockReturnThis
    • thisを返すモックを実装するときの糖衣API
    • jest.fn().mockReturnThis()

モック名

  • jest.fn().mockName("hoge name")でモック名をつけれる
  • テスト結果でエラーを出力しているモック関数を迅速に特定できる

試してみたけど、エラー時にモック名が出てるようには見えなかったなぁ。assertの種類によってことなるのかな。

カスタムマッチャ

  • モック関数がどのように呼ばれたか検査するためのカスタムマッチャ
    • これじゃない場合は、「mock」プロパティを検査する必要あある
    • mockFuncjest.fn()で定義されている場合に、mockFunc.mockに検証用の情報が格納されるやつ

snapshotだけよくわからんかったな。

test(" test custom matcher", () => {
    const myMockFn = jest.fn((arg1, arg2)=> console.log(`${arg1}_${arg2}`));
    myMockFn(1,2)
    myMockFn(2,3)
    expect(myMockFn).toHaveBeenCalled();
    expect(myMockFn).toHaveBeenCalledWith(1, 2);
    expect(myMockFn).toHaveBeenLastCalledWith(2, 3);
    expect(myMockFn).toMatchSnapshot();
});

てか、このカスタムマッチャーの方を普通は使いそう。.mockプロパティを使うとだるすぎる。

Jestプラットフォーム

なんか、Jestを利用したツールとかを作るためのAPIぽいな。

  • jest-changed-files
  • jest-diff
  • jest-docblock
  • jest-get-type
  • jest-validate
  • jest-worker
  • prettyFormat
    • なんかモジュール読み込めなかったな。

jest community

へー、awesome jestなんてものがあったのか。

GitHub - jest-community/awesome-jest: 🕶Awesome Jest packages and resources

一旦備忘メモ

chromeデバッグ

$ node --inspect-brk node_modules/.bin/jest --ru
nInBand sum.test.js

chromeを開いて「chrome://inspect」を開く。

スナップショットテスト

スナップショットテスト · Jest

  • UIが予期せず変更されてないかを確かめるのに非常に有用なツール
  • 2つのスナップショットが一致しない場合は失敗する

たぶん、スナップショットって、文字列(HTMLとか)なんだと思う。

Jestにおけるスナップショットテスト

スナップショットの更新

  • jest --updateSnapshot
  • 失敗するすべてのスナップショットテストのスナップショットを更新する
  • --testNamePatternで更新対象を限定できる

インタラクティブスナップショットモード

  • 一つ一つ差分みながら、更新できる。

インラインスナップショット

  • スナップショットを.snapファイルに出力するのではくてコード上に追記してくれる

Property Matchers

  • IDとか、日付とか、実行毎に値が変わるものがある場合
  • 具体的な値の代わりに、Any<Number>とかスナップショットに記載するとエラーにならない。
    • mockしたら良いのでは、と思った

ベストプラクティス

  • スナップショットをコードとして扱う

    • レビューする
    • テストがfailしたときに、何も考えずにスナップショットを再作成しないようにする
  • べき等性のあるテストを書く

    • 何回実行しても同じ結果になるべき
    • Date.now = jest.fn(()=> 1482363367071);のようにモック化するなど
      • 何回実行しても1482363367071を返すようになる
  • 叙述的なスナップショット名を使う

    • 「~である」という説明のような書き方
    • exports[ <username /> should render Alan Turing] みたいな。
      • 何が描画されるか読んでわかる

よくある質問(で気になったもののメモ)

  • スナップショットファイルはバージョン管理する
  • スナップショットは出力が正しいかをテストするという目的でいつでも使える
  • ビジュアル回帰テストとの違いは、ビジュアル回帰はwebページのスクリーンショットをとってピクセル単位で比較する、スナップショットはテキストファイルを比較する、目的が違う
    • 2つともUIをテストするという点では同じ
    • (目的が違う????)
  • スナップショットを手動で作成することもできるがやりやすいものではない
    • テスト駆動的に使うものではなくて、テスト対象のモジュールの出力が変更されたかをわかりやすくるもの
  • コードカバレッジはスナップショットテストでも機能する

非同期の事例

$ mkdir jest_babel_sample
$ cd jest_babel_sample
$ npm init -y
$ npm i -D jest
$ npx jest --init  #これはjest.config.jsを作るため、選択肢は適当に選んだ。てか結局中身は消した気がする。(module.exports = {}だけ残した)
$ npm run test # jestが動くようになる。jest --initでそのように選択したら
$ npm i -D babel-jest @babel/core @babel/preset-env
## babel.config.js
# module.exports = {
#   presets:[['@babel/preset-env', {targets: {node: 'current'}}]],
# };
# ./srcフォルダ以下に、sum.js, sum.test.jsを作成
# ESM形式で記述(import / export)

$ npm run test # sum.test.jsが実行される。

あー、なるほど。Babelの設定がプロジェクト内にあれば、自動的にファイルをトランスパイルして、テストを実行してくれるみたい。

babel-jestがそれを担っているのかな。

自動的にファイルをトランスパイルされたくない場合はjest.config.jstransform:{}のようにtransformの設定を明示的にリセットする。

たしかに、webpackは不要か、バンドルした結果をテストするわけではなくて、テスト対象モジュールだけをトランスパイルできればよいので、jest側でトランスパイル処理をbabel経由でするのね。(TypeScriptもそうなのかも)

nodeコマンドを--input-type=module指定で実行したらESMを実行できるかと思ったら、そんなことはなかったな。(できると書いてある記事もあったが、できなかった)

まぁ、そんなに困ることはないのか?

  • プロダクションコード:webpackでバンドルするときにトランスパイル
  • テスト:babel-jestでトランスパイル

という使い分けになるのかしら。。(package.jsonにtype=module指定したのを、src直下に置くとかもあるかも??)

非同期の事例

非同期の事例 · Jest

  • __test____mock__はモジュールと同階層に置く
    • jestでテスト実行時は勝手によみこんでテストを実行し、モックがあればモックも読む
  • モック化するには
    • jest.mock("../request") のように、モック化指定する
  • 非同期ロジックのテストを書くときはアサーション(expect)が呼ばれたかをアサートする
    • expect.assertions(1)

タイマーモック

jestjs.io

  • jestはタイマー関数を自分で時間経過をコントロールできる関数に置き換えることができる
    • setTimeout, setInterval, clearTimeout, clearInterval
    • タイマーを任意時間で発火できるということかな?
jest.useFakeTimers(); //タイマーをコントロールするだけならこれだけでよさげ
jest.spyOn(global, "setTimeout"); //setTimeoutが呼ばれたかなど検証したいならspyOnする

// 全てのタイマーを実行する
jest.runAllTimers

// こういうふうにモック化して渡すから、toBeCallledとかで検証できるのかな
  const callback = jest.fn();
  expect(callback).toBeCalled();

// 待機中のタイマーを実行する
jest.runOnlyPendingTimers() // 1回待機中のタイマーを実行する、呼ぶたぶにタイマーが進むっぽいな。

// 指定した時間でタイマーを進める
jest.advanceTimersByTime(msToRun)

マニュアルモック

  • マニュアルモックはモックデータを返す機能をスタブするために使う

ユーザモジュールのモック

  • モジュールのディレクトリ直下のmocksサブディレクトリにモックモジュールを作成
  • ./src/user.jsというモジュールをモックするなら./src/__mocks__/user.js
  • mocksは小文字

Nodeモジュールのモック

  • node_modulesと並列にmocksフォルダを作る
    • lodash.jsをモックするなら.__mocks__/lodash.jsという感じかな
      • モック実装する
        • 簡単に実装する、export default jest.createMockFromModule("lodash");
        • 独自実装するexport default { isString : jest.fn() } とか返すと、isStringだけモック化される
          • isString以外は、他のメソッドとかも消えている.
  • 上記のモックモジュールを実装しなくてもテストケース側でimport前に以下実行でモック化できる
    • jest.mock("lodash");
  • spyOnでもできる
  • ES module importするときは、importの前にjest.mock("モジュール")しなきゃいけない
    • jestが自動的に、jest.mockをコードの先頭に移動してくれる
  • JSDOMに実装されていないメソッドのモック

ES6クラスのモック

  • モック関数を使用することでモックを作成できる
  • クラス構文はいくつかのコンストラクタ関数の糖衣構文らしい

自動モック

  • jest.mock("モジュールファイル名")でモックコンストラクターになる
    • これで、newとかできるんすねぇ。

マニュアルモック

  • mocksディレクトリにモジュールファイル名.jsで配置する
  • モックの実装としてはjest.fn().mockImplementation(() => { return {xxx} }) みたいに、クラスで生成されるインスタンスの形式のオブジェクトを返す、ただし、オブジェクトのプロパティで関数があったら、jest.fn()`でモック化する

jest.mock()をモジュールファクトリ引数で呼ぶ

  • 第二引数にモジュールファクトリを渡す
  • モジュールファクトリはモック関数を返す関数
jest.mock('./モジュールパス', () => {
  return jest.fn().mockImplementation(() => {
    return { xxxxx:yyyy}
  });
});
// mockImplementationは新規オブジェクトを返す関数を定義

jest.mock()は先頭に引き上げられるから、jest.mockより上で定義した変数を参照するとエラーになるよ、みたいなこと書いてある気がする。

クラスメソッドをモック化するシンプルな例

const playSoundFileMOck = jest
  .spyOn(SoundPlayer.prototype, 'playSoundFileI)
  .mockImplementation(()=>{
    console.log("mocked function")
  });

クラス(関数)のプロトタイプメソッドをモック化する。

モックをスパイする

  • jest.fn()でモック関数を作る
//default exportの場合
import Hoge from './hoge'
jest.mock('./hoge', () => {
  return jest.fn().mockImplementation(() 0 => {
    return { fuga : () => {}}
  })
)

// default export以外を持っくする
import { Hoge } from './hoge'
jest.mock('./hoge', () => {
  return {
    Hoge : jest.fn().mockImplementation( () => {
       return { fuga: () => {}}
    }),
   }
})

クラスのメソッドをスパイする

  • クラスメソッドが期待した引数でよばれているかを確認するには、クラスメソッドをスパイする必要がある

テストでモックコンストラクタが呼ばれると、その都度新しいオブジェクトが生成される。すべてのオブジェクトのメソッド呼び出しをスパイするには、モックオブジェクトが参照するモック関数インスタンスを同一にする

const mockFuga = jest.fn()
jest.mock('./hoge', () => {
  return jest.fn().mockImplementation(() 0 => {
    return { fuga : mockFuga}
  })

テスト間でのクリーンアップ

  • [モックオブジェクト].mockClear()
    • 呼び出し記録をクリアする

備忘メモ

  • モック関数はjest.fn()で定義する必要がある。
    • そうしないと、呼ばれたかどうかとかのアサートができない
  • モックの実装をするには
    • jest.fn().mockImplementation*1
      • 省略記法もある、jest.fn*2
  • spyOnでスパイ実装、スパイはロジックを戻せる
    • var spy = jest.spyOn(モジュール, "メソッド名").mockImplementation*3
      • spy.mockRestore()で、実際のロジックに戻せる
  • jest.mock("axios")でモジュールをモック
    • axios.get.mockResolveValue(xxxx) でaxios.getが返す値を定義できる

参考リンク

モジュールモックのバイパス

  • jest.mock('hoge')でモジュールをモックするが全部モックされてしまう
  • hogeモジュールのfuga関数だけは実際のロジックを利用ということもできる
jest.mock('hoge')
import hoge from 'hoge'
const { fuga } = jest.requireActual('hoge')

ECMAScript Modules

  • experimantalな機能らしい。
  • code transformsを明示的にdisableする。transform: {}
  • nodeを実行時に--experimenta-vm-modulesを付ける

こまかいところはちょっとわからんが。babelやTypeScriptで実行した方がサポートされているから良さそう。

  • Using with webpack

    • webpackと利用する場合、ちょっと情報古そうな気もする。
    • どういうケースで利用できるのかよくわからん
  • DOM操作

    • 直接DOM操作するコードはテストが難しいとされる
    • ボタンを押下したらバックエンドあらfetchしてdomにhtml追加とか
  • jest-environment-jsdomを使う
  • npm i -D jest-environment-jsdomこれだけでいいんかな
  • testEnvironmentプロパティにjsdomを指定しないといけないっぽい
  • テストメソッドの上部にコメントでenvironmentを指定する方法もあるみたいだけど、うまく認識してくれなかった。なぜでしょう

jsdomはJS上でDOMを再現(イベントの監視とか、domのデータ構造の再現とか)してくれるみたい。

サンプルコード実行してみたら、動いたな。テストコードではjsdomは意識しなくても良いみたいだ。testEnvironmentで指定しているからか、テストコード上でdocument.body.innerHTML でHTMLを突っ込んだら、それがDOMとして認識してくれるみたい。

つまり、テストコード上でブラウザで実行しているのと同じ感覚で実装できるということか。

アーキテクチャ · Jest

よし、終わりにしよう!ながかった、気長にちょこちょこやってましたね。 jestだけでなくて、nodeでのモジュール機構のキャッチアップにもなったし、結構ためになった。

やっぱり、手を動かして動作確認しつつやってたのが良かった。

*1:) => console.log("hoge"

*2:) => console.log("hoge"

*3:)=>console.log("hoge"