Adobe ExtendScriptにユニットテスト環境を構築する
遂にUXPがInDesignにも来ましたが今の開発の流れを見る限りIllustratorその他のアプリケーションにも本格的に実装される日はまだまだ長そうです。しかし規模が大きくなってくるとExtendScriptの開発もやはり効率化したくなるものです。特にCEPとくっ付いたExtendScriptのテストの実行は色々大変で各関数、オブジェクトに問題が出てくる度にコードを一部分だけ抜き取ってテストするのもだれます。ということで今回ExtendScriptにユニットテストを実装してみました。但しそんなに本格的なものを実装したわけでは無いのでご了承ください。私自身個人開発の域を超えない範囲の開発しかしてませんので。CEPも長くて2年くらいの寿命でしょうし。またとりあえず簡単なテストが逐次できればいいかなという安易な動機から始まってますので。
そもそもなぜテストをする?
コードの規模が大きくなってくるとバグの原因となる箇所も多くなってきます。仮にバグが見つかったところでどこの箇所がバグの原因になっているのかわかりづらいこともあります。なのでコードの規模がある程度の大きさになってくるとモジュール単体できちんと正確に動いているかどうか確認したくなることが多々出てきます。ある関数にある値を渡すと正常に動くけど違う値を渡しても正常に動くのか?といった細かいテストはモジュール事にテストした方が効率的でしょう。
開発環境
また今回始める際に以下の環境が構築できている、また理解できていることが前提となります。
CEP上のExtendScriptをモダンな環境で開発する(Webpack + TypeScript + Babel)
ES2015以降、Babel+TypeScript+webpackの環境でテストを行います。
通常のExtendScriptはモジュールが使えないので個別に関数、オブジェクトをimportすることができませんでしたがモダンな環境ならばこれが実現できるので個々のテストがやりやすくなっています。
またテストの実行にはExtendScript Debugger ver2を使用します。
ExtendScript Debugger ver2 触ってみた その1
Jestっぽいシステムの実装
よくあるJestっぽいテストを実装します。でも何度も言いますがそこまで本格的ではないです。勿論Jestそのものが使えればそれに越したことはないのですがJestがExtendScriptのエンジンで動くわけないので自作します。
仕組みとしてはwebpackで実際に使用するコードとは別にテスト用のコードをコンパイル、書き出してExtendScript Debugger ver2で実行という感じです。具体的には以下のように。
mainとmainTesファイルで別々に書き出しています。mainが本番用のコード、mainTestがテストの用のコードです。webpackで同時に書き出しています。テスト用のコードはtest.handle.jsで書き出されるのでlaunchでテスト用のコードが実行できるようにしておくと直ぐにテストできます。
実際のコード
それではテスト用のコードをJest風味に書きます。
describeでグループ化、expectで結果を渡してtoBe等で結果が思い通りのものかどうか判定します。あくまでJest風味なので完全再現とかしてません。私にはできません。
テスト用のクラス
まずテスト用のクラスを以下のように実装しています。
- Description
- Test
- Result(expect)
まずはJestは引数で受け取ったコールバック関数を実行しつつチェーンメソッドでつなげています。
チェーンメソッドはオブジェクト自身を返すメソッドで実装できます。
valueの値を引き継いで次のチェーンメソッドで処理を繋いでいます。
ただthisでオブジェクト自身を返すのでアロー関数は使えません。
Testの場合以下のようになります。
インスタンスを生成するときにコールバック関数とテストの内容の記述を受けています。report.logメソッドは単に$.writelnでコンソールを書くためのメソッドです。 CEP上のExtendScriptをモダンな環境で開発する(Webpack + TypeScript + Babel)を参照。
isSkipはご存知テストをスキップするかどうかのプロパティです。skipメソッドを実行するとテストをスキップするようにします。また実際のテストの実行はチェーンメソッドでtestingで実行します。その他配列形式で引数を渡したい時はsetEachで値を渡します。
expect
expectはResultというクラス(Expectationでもよかった?)を使います。詳細は以下の通り。
staticでいくつかプロパティとメソッドを持たせていますがこれはJestでいうところのbeforeEachとかafterEachです。staticで
定義して全てのインスタンスで同じように動くようにします。beforeEachにドキュメントを作成するメソッドを定義すれば
テスト実行前に毎回ドキュメントを作成します。テスト実行前に空のドキュメントが必要な時とかこういうテスト環境があると便利です。
全てオフにしたい時にはresetEachFuncを実行。その他toBeやtobeFalsy、tobeTruthyとお馴染みのマッチャーです。勿論現在ほとんどありませんが必要だったら自分で自作して追加しようかとは思います。describeのDescriptionクラスも似たような感じです。
実際のサンプルコードは以下のようになります。
GitHubに実際のコードを公表していますのでファイル構成等ご覧ください。