モジュールじゃない JavaScript コードを Vitest でテストする
お題
ぜんぜんモジュールじゃない、以下のような JavaScript ファイルについて、Vitest でテストを書くことになりました。
function sum(a, b) {
return a+ b;
}
Vitest テストプロジェクトを立ち上げる
まずは以下のようにして、test
フォルダに Vitest のテストプロジェクトを新規作成します。
$ mkdir test
$ cd ./test
$ npm init -y
$ npm install --save-dev vitest
$ _
続けて、この test
フォルダ内に生成された package.json
を編集し、npm run test
を実行すれば Vitest のテストランナーによってテストが実行されるようにします。
{
"name": "test",
"version": "1.0.0",
"main": "index.js",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "vitest"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"vitest": "^2.1.8"
}
}
テストコードを記述する
次に、実際のテストコードを書きます。test
フォルダ内に sum.test.ts
を作成し、以下のように記述します。エディタは、自分は主に VSCode を使っているので、下記コードも VSCode で編集しました。
import { describe, test, expect } from 'vitest';
import '../app/sum';
describe("test for sum.js", () => {
test("sum(1, 2) should return 3", () => {
expect(sum(1, 2)).toBe(3);
});
});
前述のとおり、app/sum.js
は古典的な JavaSrcipt コードで、グローバル名前空間に "sum" という名前の function をじかに宣言しています。このような ESModule 形式でない JavaScript ファイルを、Vitest のテストコードからどうやって参照するのかがよくわかりませんでした。
とりあえず恐る恐る import '../app/sum';
と記述してみたところ、とりあえず VSCode エディタ画面上はとくにエラーが表示されることはなく、また、下図のように sum.js
内で定義されている sum
関数がコード補完の候補に出てきますし、また、その関数シグネチャも正しく表示されましたので、この方法でいいのかな、と思いました。
"sum is not defined"
テストを実行する... が さて、テストコードの実装を終えましたので、テストを実行してみました。ターミナルから npm test
を実行することで、あるいは VSCode に Vitest の拡張をインストールしてある場合は、VSCode の Test Explorer から、テストを実行できます。
すると、残念ながら、"sum is not defined"
、関数 sum は定義されていない、というエラーになってしまいました。
VSCode エディタ画面上での判断と、Vitest によるテストコードの実行時とでは、import
の処理の仕方が違うようです。
ChatGPT に聞いたり試行錯誤するもうまくいかず
詳細は割愛しますが、ChatGPT に聞きながら、vitest.config.ts
や tsconfig.json
を用意したり、それらをいろいろひねくりまわしてみましたが、どうにもうまく "sum is not defined"
を解決できませんでした。
結局 eval 使いました
どうにもうまくいかなかったので、ChatGPT からの回答の最後のひとつにあった方法、app/sum.js
の内容を文字列として変数に読み取り、 eval
を使ってその文字列を評価する、という方法で、どうにかテストを実行することができました。具体的には、次のように進めました。
このテストコードは、(とくに環境を明示しませんでしたので) Node.js 上で動作していることから、Node.js の fs
モジュールなどを使って JavaScript ファイルの内容を読み取ることにしますので、ターミナルから、以下のように Node.js モジュールの TypeScript 型定義を追加しておきます。
$ npm install --save-dev "@types/node"
$ _
その上で、テストコードを以下のように実装します。つまり、以下のことを行ないます。
-
app/sum.js
ファイルの内容を、Node.js のfs
モジュールを使って文字列として読み込み、 - その文字列の末尾に
sum;
を付け加えて、この JavaScript 文字列を評価したときに、sum
関数が返るように加工し、 - 加工後の JavaScript 文字列を
eval
関数に渡して評価実行、 -
eval
による評価結果、すなわち同関数の戻り値を変数sum
に格納 -
sum
を使用してテスト実施
import { readFileSync } from 'fs';
import { describe, test, expect } from 'vitest';
const scriptContent = readFileSync('../app/sum.js', 'utf-8') + `\n sum;`;
const sum = eval(scriptContent) as (a: number, b: number) => number;
describe("test for sum.js", () => {
test("sum(1, 2) should return 3", () => {
expect(sum(1, 2)).toBe(3);
});
});
以上でどうにかテストが実行できるようになりました。
本当にこれでいいの?
とりあえず動いたわけですが、eval
を使うなど、なんだか負けた感じがしてたまりません。もっとまともな方法があれば知りたいです。
Discussion