1. ホーム
  2. javascript

[解決済み] Jestでグローバルをモックする

2023-05-01 14:24:30

質問

Jestでグローバルオブジェクトをモックする方法はありますか? navigator のようなグローバルオブジェクトをモックする方法はありますか、あるいは Image *? 私はこれをかなりあきらめて、一連のモック可能なユーティリティ・メソッドに任せています。例えば

// Utils.js
export isOnline() {
    return navigator.onLine;
}

この小さな関数をテストするのは簡単ですが、厄介で、全く決定論的ではありません。75%は実現できますが、これくらいが限界です。

// Utils.test.js
it('knows if it is online', () => {
    const { isOnline } = require('path/to/Utils');

    expect(() => isOnline()).not.toThrow();
    expect(typeof isOnline()).toBe('boolean');
});

一方、このインダイレクトをよしとするならば、今度は navigator をこれらのユーティリティ経由でアクセスできるようになります。

// Foo.js
import { isOnline } from './Utils';

export default class Foo {
    doSomethingOnline() {
        if (!isOnline()) throw new Error('Not online');

        /* More implementation */            
    }
}

...そして決定論的にはこのようにテストします...

// Foo.test.js
it('throws when offline', () => {
    const Utils = require('../services/Utils');
    Utils.isOnline = jest.fn(() => isOnline);

    const Foo = require('../path/to/Foo').default;
    let foo = new Foo();

    // User is offline -- should fail
    let isOnline = false;
    expect(() => foo.doSomethingOnline()).toThrow();

    // User is online -- should be okay
    isOnline = true;
    expect(() => foo.doSomethingOnline()).not.toThrow();
});

私が使ったすべてのテストフレームワークの中で、Jestは最も完全なソリューションのように感じますが、テスト可能にするために厄介なコードを書くときはいつでも、テストツールが私を失望させているように感じます。

これは唯一の解決策でしょうか、それともRewireを追加する必要があるのでしょうか?

*にやにやしないでください。 Image はリモートのネットワークリソースに ping を打つのに最適です。

どのように解決するのですか?

すべての テストスイート はそれぞれ独自の環境を実行するので、グローバルを上書きすることでモックを作ることができます。すべてのグローバル変数にアクセスするには global の名前空間でアクセスできます。

global.navigator = {
  onLine: true
}

上書きは現在のテストにのみ影響し、他のテストに影響を与えることはありません。また、これは Math.random あるいは Date.now .

の変更により jsdom の変更により、このようにグローバルをモックしなければならない可能性があることに注意してください。

Object.defineProperty(globalObject, key, { value, writable: true });