Spring Boot + npm + Geb で入力フォームを作ってテストする ( その95 )( eslint を 6.8.0 → 7.32.0 へ、jest を 26.0.1 → 27.2.4 へバージョンアップする )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その94 )( Node.js を 12.16.3 → 14.18.0 へバージョンアップする ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- eslint を 6.8.0 → 7.32.0 へ、jest を 26.0.1 → 27.2.4 へバージョンアップします。
参照したサイト・書籍
package.json に記載されているパッケージのバージョンアップ方法 【 npm-check-updates, outdated 】
https://qiita.com/sugurutakahashi12345/items/df736ddaf65c244e1b4fTesting Asynchronous Code
https://jestjs.io/docs/asynchronousaxios - Cancellation
https://github.com/axios/axios#cancellationNode.js v15に実装されたAbortController
https://www.mitsue.co.jp/knowledge/blog/frontend/202012/14_0900.html
目次
- eslint を 6.8.0 → 7.32.0 へ、eslint-config-airbnb-base を 14.1.0 → 14.2.1 へ、eslint-config-prettier を 6.11.0 → 8.3.0 へ、eslint-plugin-import を 2.20.1 → 2.24.2 へ、eslint-plugin-prettier を 3.1.3 → 4.0.0 へバージョンアップする
- jest を 26.0.1 → 27.2.4 へバージョンアップする
- テストが失敗した原因を解消する
The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string. Consider using the "jsdom" test environment.
jQuery requires a window with a document
Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise.
Jest has detected the following 2 open handles potentially keeping Jest from exiting:
jest.setTimeout(...);
を呼び出しても効かないようなので jest.config.json に testTimeout を設定する方法に切り替える
手順
eslint を 6.8.0 → 7.32.0 へ、eslint-config-airbnb-base を 14.1.0 → 14.2.1 へ、eslint-config-prettier を 6.11.0 → 8.3.0 へ、eslint-plugin-import を 2.20.1 → 2.24.2 へ、eslint-plugin-prettier を 3.1.3 → 4.0.0 へバージョンアップする
以下のコマンドを実行して eslint 関連のモジュールを最新のバージョンにします。
npm install --save-dev eslint@7.32.0
npm install --save-dev eslint-config-airbnb-base@14.2.1
npm install --save-dev eslint-config-prettier@8.3.0
npm install --save-dev eslint-plugin-import@2.24.2
npm install --save-dev eslint-plugin-prettier@4.0.0
npm test
コマンドを実行するとテストは全て成功し、
npm run build
コマンドも正常に終了しました。
途中で npx browserslist@latest --update-db
のコマンドを実行するようメッセージが出力されているので、実行します。npm update
は eslint 以外のモジュールもバージョンアップされるので(しかも必ずしも最新バージョンになる訳ではないらしい)実行しません。
jest を 26.0.1 → 27.2.4 へバージョンアップする
以下のコマンドを実行します。
npm install --save-dev jest@27.2.4
npm install --save-dev jest-html-reporter@3.4.1
レポートファイルを削除したいので最初に gradle から clean タスクを実行しておきます。
npm test コマンドを実行するとテストが大量に失敗するようになりました。。。
build/reports/jest/jest-html-reporter.html にレポートが出力されていますが、failed の赤ばかりです。
エラーになったテストで出力されているメッセージは全部で4つあり、1つ目は The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string. Consider using the "jsdom" test environment.
。
2つ目は jQuery requires a window with a document
。
3つ目は Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise.
。
4つ目は Jest has detected the following 2 open handles potentially keeping Jest from exiting:
。
テストが失敗した原因を解消する
The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string. Consider using the "jsdom" test environment.
エラーメッセージに出力されている https://jestjs.io/docs/configuration#testenvironment-string を見ると The test environment that will be used for testing. The default environment in Jest is a Node.js environment. If you are building a web app, you can use a browser-like environment through jsdom instead.
という記述があり、テストファイルの先頭に @jest-environment docblock を追加して @jest-environment jsdom
を記述すれば解決するようですが、さすがに各テストファイルに記述するのは避けたいところです。
jest.config.json に "testEnvironment": "jsdom",
を追加することにします。
{ "coverageDirectory": "build/reports/jest", "moduleDirectories": [ "node_modules", "src/main/assets/js" ], "testEnvironment": "jsdom", "reporters": [ "default", [ "./node_modules/jest-html-reporter", { "pageTitle": "Test Report" } ] ] }
また Jest 27: New Defaults for Jest, 2021 edition に For this reason, we are changing the default test environment from "jsdom" to "node".
という記述がありました。For this reason の前の文章を読むと jsdom はパフォーマンス・オーバーヘッドがあるので default を node に変更したとのこと。今後は必要なテストだけテストファイルの先頭に @jest-environment docblock を追加する方式にした方が良いのでしょう。
jQuery requires a window with a document
testEnvironment を jsdom に変更したら出力されなくなりました。
Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise.
https://jestjs.io/blog/2020/05/05/jest-26#other-breaking-changes-in-jest-26 に [jest-circus] Fail tests if a test takes a done callback and have return value
という記述がありました。
done callback を使っているとテストが失敗するようになったことは分かりましたが、これだけでは対応方法が分かりません。他に参考になるものがないか調べてみると Done.fail function is not working の中に Testing Asynchronous Code へのリンクがありました。このページに対応方法が記載されています。
リンク先の記述に従って修正すると以下のコードの場合には、
test("Nock で 500 を返すと catch の方で処理される", async (done) => { nock("http://api.openweathermap.org") .get(/^\/data\/2\.5\/weather/) .reply(500); await openWeatherMapHelper .getCurrentWeatherDataByCityName("Tokyo") .then((response) => { done.fail("then に処理が来たらエラー"); }) .catch((e) => { expect(e.response.status).toBe(500); }); // test("...", async done => { ...}); と done を記述している場合には、テストの最後で done(); を呼び出さないと // 5秒待機した後、 // Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL. // のエラーメッセージを表示してテストが失敗する // ※https://facebook.github.io/jest/docs/en/asynchronous.html#callbacks 参照 done(); });
async/await を取り除いて Promise を返すようにするか(Promise を返せば Jest は Promise が完了するまで待つとのこと)、
test("Nock で 500 を返すと catch の方で処理される", () => { expect.assertions(1); nock("http://api.openweathermap.org") .get(/^\/data\/2\.5\/weather/) .reply(500); return openWeatherMapHelper .getCurrentWeatherDataByCityName("Tokyo") .catch(e => expect(e.response.status).toBe(500)); });
async/await を残すならば try...catch で以下のように記述すればテストが成功するようになります。
test("Nock で 500 を返すと catch の方で処理される", async () => { expect.assertions(1); nock("http://api.openweathermap.org") .get(/^\/data\/2\.5\/weather/) .reply(500); try { await openWeatherMapHelper.getCurrentWeatherDataByCityName("Tokyo"); } catch (e) { expect(e.response.status).toBe(500); } });
今回は後者の async/await を残して try...catch で対応することにします。
async (done)
→async ()
に変更する。done();
あるいはdone.~
を呼び出している箇所を削除する。- テストの最初に
expect.assertions(1);
を記述する(assertions メソッドに渡す数字はテストで expect が呼び出される回数を指定する)。https://jestjs.io/docs/expect#expectassertionsnumber を読むと async を付けたテストメソッドの場合、きちんとテストが実行されているのかを確認するために expect.assertions を書いた方が良いらしい。 - await を付けて呼び出しているところを、await を取り除いて try...catch で囲む記述に変更する。
Jest has detected the following 2 open handles potentially keeping Jest from exiting:
package.json で jest を実行するコマンドに --detectOpenHandles
オプションを付けているのでテスト終了時にオープンされたままのハンドラが検知・出力されるのですが、以前から付けていて Jest 27 にバージョンアップされたらこれまで検知されていなかったものが検知されるようになったようです。
"scripts": { "test": "run-s prettier:format prettier:format-test jest", "jest": "jest --config=jest.config.json --coverage --runInBand --detectOpenHandles", ..........
axios のリクエストを cancel する方法が https://github.com/axios/axios#cancellation に記述されており、これを使えば解消しそうな気がするのですが、うまく実装できません。。。 テストは成功しているので、今回は特に何もしないことにします。
また https://jestjs.io/docs/cli#--detectopenhandles によると --detectOpenHandles
はデバッグ時のみ使用するよう書かれていたので、テストが並列で実行されなくなる --runInBand
オプションと合わせて外すことにします。
"scripts": { "test": "run-s prettier:format prettier:format-test jest", "jest": "jest --config=jest.config.json --coverage", "jest:debug": "jest --config=jest.config.json --coverage --runInBand --detectOpenHandles", ..........
jest.setTimeout(...);
を呼び出しても効かないようなので jest.config.json に testTimeout を設定する方法に切り替える
上記4つの調査でいろいろ試している時に気づいたのですが、テストメソッド内で jest.setTimeout(30000);
を呼び出しても 5秒経過するとテストがエラーになりました。jest.config.json に "testTimeout": 30000,
を設定すれば有効になるようなので、こちらの設定に切り替えます。
{ "coverageDirectory": "build/reports/jest", "moduleDirectories": [ "node_modules", "src/main/assets/js" ], "testEnvironment": "jsdom", "testTimeout": 30000, "reporters": [ "default", [ "./node_modules/jest-html-reporter", { "pageTitle": "Test Report" } ] ] }
npm test コマンドを実行すると A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks.
というメッセージが出力されますが、テストは全て成功するようになりました。
build/reports/jest/jest-html-reporter.html のレポートも passed の緑のみになっています。
履歴
2021/10/03
初版発行。