📖

Jestでno-moduleJSをテストする

2022/04/11に公開

IIFEとかUMDは厳しそう

関連記事
Jestで簡単ユニットテスト
JestをES6化してimport/exportを使う方法


------------------- ↓ 前書きはここから ↓-------------------

Jestでユニットテストを実践するようになって、
いろいろ不便な点がでている。
一番困るのがデータベース周りで、
どれを使っても煩雑になってしまう感じだ。
(フルスタックフレームワークなど無かった)

そしてもう一つ大きく困る点が、
昔ながらのJSファイルをテストできないことだ

昔ながらのJSファイルとは、
ロードするだけで動いてしまうタイプのコードだ。
(英語でもno-moduleとかwithout moduleとか名前がない)

ES6(export/import)登場以降は徐々に減っているが、
残っているところには残っている。

今回はそんなファイルをテストする方法を模索する
鍵となるソリューションは**VM Module**だ

ヾ(・ω<)ノ" 三三三● ⅱⅲ コロコロ♪


------------------- ↓ 本題はここから ↓-------------------

JEST実行環境

こちらを参照
JestをES6化してimport/exportを使う方法

テスト開始

失敗する例

以下のようなプログラムがあったとする、。

iife01.cjs
const sum = function (a, b) {
    return a + b;
}

var abc = 'def'

これをJestにかけるにも呼び出し方法がない。
試しにimportを使ってみると

iife01.test.mjs
describe('iife01.test.js',() => {
    test('find sum', async () => {
        const modules = await import('iife01')
        console.log(sum)
    })
})

結果

    ReferenceError: sum is not defined

       9 |     test('abc', async () => {
      10 |         const modules = await import('iife01')
    > 11 |         console.log(sum)
         |                     ^

(・ω・) ですよねー

sum が定義されていないと言われてしまう

VM Moduleを使う場合

node本体にサンドボックス機能がついている
外部に影響の内容にjavascriptを実行する仕組み

javascriptファイルをテキストファイルとして取り出し、
evalやFunctionにかける方法もなくはないが、
テスト実行環境に反映されたり悪影響も多いので、
nodeのサンドボックス環境内で実行するようにする。

iife02.test.mjs
import fs from "fs";
import {Script, createContext} from "vm";

describe('iife02.test.js',() => {
    test('find sum', async () => {
        const code = fs.readFileSync('iife01.cjs');
        const script = new Script(code.toString());

        const context = {};
        createContext(context);
        script.runInContext(context);
        console.log(context)
    })
})

実行してみると

npm run test 
  console.log
    { sum: [Function: sum], abc: 'def' }

contextに定義されている sumabc が取り出せているのがわかる

rewireモジュールを使う場合

調べているとrewireというモジュールを見つけた。
内容的には上記サンドボックスと同様のようで、
整備は楽かもしれない。

インストール
npm i -D rewire

また、babel-plugin-rewireというのもあって、
babel使ってるユーザーにはいいかもしれない。

iife03.test.mjs
import rewire from "rewire";

describe('iife02.test.js',() => {
    test('find sum', async () => {
        const modules = rewire(file)
        console.log(modules.__get__('sum'), modules.__get__('abc'))
    })
})

実行してみると

npm run test 
  console.log
     [Function: sum] def

(・o・) おー

サンドボックスと同じ結果がでている

vm2モジュールを使う場合

上記サンドボックスと拡張したvm2というモジュールがある
(名前もうちょっとどうにかしてせいよ)
内容は正直深掘りしていないのでインストール方法だけ載せておく

インストール
npm i -D vm2

------------------- ↓ 後書はここから ↓-------------------

rewireの使い勝手がいいので、
一見するとこちらを使いそうだが、
昔のコードにありがちな DOM操作べた書き は対応できない

なので、基本的にはサンドボックス、あるいはvm2で良いかなと思う
DOMテストについては別途記事にする。。。やも。

IIFEやUMDをテストすることはできない

IIFEとは即時実行関数というが、
以下のようなコードを見たことがあるかもしれない。

IIFE
(function (param) {
  var abc = param
})(param)

ベタに書くと名前空間が大変なことになるので、
クロージャーの中にコードを書き、
クロージャーはロードとともに即実行するという記述方法。
ESM普及まではむしろ主流だった

UMDはそれを拡張したもので、
typescriptやbabelのコンパイル結果でよく見かけるものだ。

ただ、残念ながらこれはコードを書き換える以外にテストする方法がない。

Discussion