2020-05-15 TypeScript 3.8 -> 3.9 でテストが失敗した
TypeScript 3.8 から 3.9 にバージョンアップしたらテストが失敗した。
イメージとしては↓のような形だった。
// mod_f.ts
export function f(): void {
console.log("Hello");
}
// mod.ts
export { f } from "./mod_f";
// main.ts
import { f } from "./mod";
export function main() {
f();
}
// main_test.ts
import * as assert from "assert";
import * as sinon from "sinon";
import * as mod from "./mod";
import { main } from "./main";
const f = sinon.stub(mod, "f");
main();
assert(f.callCount === 1); // ここで失敗 callCount === 0 になった
mod
が全体で共有される object であることを利用している。 sinon.stub
で mod.f
を差し替えることで main.ts
の import
している f
を置き換えてテストしている。
これがなぜ TypeScript 3.9 で壊れるのかパッと見では分からない。
原因は TypeScript 3.9 の Breaking Changes の Exports Now Use Getters for Live Bindings にある。 https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#breaking-changes
--module commonjs
を選択したときに export ... from ...
したものは getter として定義されるようになった。
また sinon.stub
は Object.defineProperty(o, { configurable: true, value: /* ... */ })
な property を置き換えることができる。しかし Object.defineProperty(o, { configurable: true, get: () => /* ... */ })
を置き換えることができない。
これらの組み合わせによって sinon.stub(mod, "f")
が機能しなくなったことでテストに失敗した。
ひとまず sinon.replaceGetter
を使うことで回避した。……だけどモヤモヤしている。
↑に関連して defineProperty を 2 回呼び出したときの挙動などでいろいろハマった。そちらは Twitter に書いた。