Jest で fail したときのコードスニペットの行番号がずれる問題に悩まされる

Jest でテストコードを書いていたら地味に嫌な挙動に悩まされてしまったので、その現象をメモしておきます。

前提条件

  • TypeScript で Jest を用いたテストコードを書いている
  • node-config を用いて環境ごとの設定を変更できるようにしている
    • コンフィギュレーションファイルを TypeScript で記述している
  • Jest でのテスト実行に ts-jest を利用する

遭遇した現象

(以下は問題を再現させるためのテストコードなので内容はあまり気にしないとして、) このテストコードを npx jest のようにして実行します。なお現象を再現させるコードすべてを含んだのリポジトリは こちら です。

import * as config from 'config';

const msg = config.get('message') as string;

test('foo', async () => {

  // 当然これは fail しない
  expect(1).toBe(1);

  // これだって fail しない
  expect({a: 1, b: 2}).toEqual({a: 1, b: 2});

  // ここで fail することを期待している
  expect(msg).toEqual("Hello, world.");

});

すると、以下の図に示すように fail した箇所を指し示すコードスニペットにて間違った行を指し示してしまう (fail したと示される箇所の行番号が実際の TypeScript と異なる) 現象が発生します。

Fail した箇所のコードスニペットを含む結果

これは WebStorm などの IDE 上でテストコードを実行した場合も同様で、スタックトレースをクリックして fail した行にジャンプしようとしても、やはり間違った行にジャンプしてしまうといった問題に遭遇します。

なおエラーメッセージ自体は正しい結果となっており、行番号に由来する諸々が壊れてしまっているように見受けられます。

判明している事実

いまのところ明らかになっているのは、node-config のコンフィギュレーションファイルを TypeScript で記述していて、テストコードで (もしくはテスト対象のプロダクションコード側でも?) node-config を利用 した場合にこの問題が発生する、ということです。

これを深堀りして調べてみると、どうやら TypeScript で記述されたコンフィギュレーションファイルを node-config がロードする際に利用している ts-node がこの問題に影響を及ぼしていそう だということがわかりました (node-config は、TypeScript のコンフィギュレーションファイルをロードするために ts-node を require して利用 (register()) しています)。

また、ずれた行番号について調べてみると、どうやら トランスパイルされた結果の JavaScript コード側における当該アサーションの行番号 に一致することがわかりました (これは偶然の結果かもしれませんが…)。

したがって、ts-node によって TypeScript のコードが JavaScript にトランスパイルされる ことが何かよからぬ影響を生み出しているのかな… と考えています (実際に、node-config 使わずテストコード内で ts-node を require してみたら同様の問題が発生したので、ts-node が影響している (ように見える) ことはほぼ確実です)。

問題の回避方法

node-config が利用する ts-node をどうこうできれば解決できる問題なのかもしれませんが、生憎良い手段が見つかっていません。したがって、手っ取り早い回避手段としては TypeScript でのコンフィギュレーション記述を諦めて JS or JSON で記述する になります。

もしくは node-config 以外のライブラリを使う になるかと思いますが、なにぶん TypeScript 歴が浅いがためによい alternative なライブラリを把握しておりません。どなたか良質なコンフィギュレーション系ライブラリの情報を教えてください…