脱Node.js

2022/05/04

JavascriptコードのテストはさすがにJavascriptで書いていたのだが、いまそれを捨ててE2Eテストに移行している。
E2Eテストは、実際のアプリケーションやサーバー構成を再現した環境で実行するテストを指し、今回の最大の違いはブラウザ上で動作させる点だ。

これまでは、jsdomというJavascriptで書かれた模擬ブラウザ環境で実行してきた。
アプリケーションの複雑化が進むにつれて挙動が不安定な部分が増えており、同じテストが通過したり失敗したりする動作になっていた。
そしてこれはアプリが不安定なわけではなくjsdomだけで起きるエラーであったため、テストコードを調整するという無駄な現象を引き起こす。

さらに、あるライブラリのバージョンアップにより、そもそもテスト実行が不可能になった。
現時点のJavascript開発はNode.js上で動作させるものが大半であり、Node.jsのモジュール形式であるCommonJSを前提としているところがある。
近年、ブラウザにESModuleという新たな標準が導入されたことで、ライブラリごとの採用モジュールが変わりつつある。

jsdomはNode.jsを前提としているため、一部のライブラリがCommonJSをサポートしないという展開にもろかった。
もはやテストは開始すらしない。
世の中一般では顕在化していないが、今後同様の壁に直面するプロジェクトは増えるだろう。

テスト向けのビルドを工夫してなんとか既存のテストを活かす手もある。
ただ先々の展開を考えると、モジュールの混在はより複雑な組み合わせになるだろうし、そもそもjsdomのエミュレートの品質が良くない。

これまでにテストに手間どっていたケースが多発していたため、この機にjsdomはもうやめようと考えた。

E2Eテストのために用意すべきものは多い

E2Eテストは実際の環境とほぼ同等と言えるため、もともと導入したいとは思っていた。サーバー一式とアプリケーション配信を完備する必要があるため着手していなかった。
今回はJavascriptで書かないと決めた以上、E2E以外に手がないので1つひとつ地道に構築することとなった。

バックエンドプロセスについては完全にコンテナ化しているため、設定以外はデッドコピーで構築できた。よってサーバー側は、主にファイル配信を追加開発した。

ブラウザの実行環境は、Javascriptの延長でまずdeno-puppeteerを構築してみたが、これは動作しなかった。
denoのテストランナーは今インターフェースが決まった段階で、使いやすそうではあるがまだ非同期処理のテストが事実上動作しない。

けっきょく、E2Eフレームワークには実績のあるSeleniumを採用した。
Seleniumは複数の言語をサポートしており、もっともメジャーなLL言語のPythonを選択。

今までPythonは体系的に使ったことはなくRubyの方が慣れている。ただ、Rubyはディストリビューションに最新版が入らないことが多く、動作環境の再現にやや難を感じている。
直近リリースされたUbuntu22.04LTSにも、昨冬版のRuby v3.1ではなく一昨年のv3.0が入っている。

Pythonはさすがにシェアが高いだけありストレートに動作する。ここまでテストケースを移植した範囲では、奇妙な動作がなく安定している。
Seleniumが呼び出すブラウザを通した挙動もjsdomより圧倒的に安定しており、ゆっくり実行させるための不毛なコードを一掃できた。

WebAssemblyもモックではなく実際にテスト対象に追加できるようになった。

脱Node.jsは相当進んだ

ソフトウェアは開放系であるため、利用パーツの変化に合わせてプロジェクト構成を大幅に変えていくのは当然と言える。
ただ、同じものを2回実装しているのだから、初回は失敗だったと認識している。

最初から、またはもっと初期段階でSeleniumで実装しておく手はあった。

Javascript開発ではNode.jsを組み込むことになりがちなのだが、Node.jsは依存しただけ後の問題に帰結している。
ここまでにビルドは既にDenoに移行しており、今回テストもPythonになった。

あと残っているのは、npmによるパッケージ管理だけだ。npmは品質に直結しないが、gitの挙動に難があり問題はすでに起きている。
npm派生を除外するとパッケージ管理のオルタナティブはDenoしかないので、ImportMapsを真面目に評価して、可能であれば早期にNode.js完全撤廃を達成した方が良いと考えている。

⁋ 2022/05/04↻ 2022/05/04