Jestを使用してJavaScriptウィンドウオブジェクトをモックする方法は?


104

ブラウザで新しいタブを開く機能をテストする必要があります

openStatementsReport(contactIds) {
  window.open(`a_url_${contactIds}`);
}

ウィンドウのopen関数をモックして、正しいURLがopen関数に渡されていることを確認したいと思います。

Jestを使用して、をモックする方法がわかりませんwindowwindow.openモック関数で設定しようとしましたが、うまくいきません。以下はテストケースです

it('correct url is called', () => {
  window.open = jest.fn();
  statementService.openStatementsReport(111);
  expect(window.open).toBeCalled();
});

しかし、それは私にエラーを与えます

expect(jest.fn())[.not].toBeCalled()

jest.fn() value must be a mock function or spy.
    Received:
      function: [Function anonymous]

テストケースはどうすればよいですか?任意の提案やヒントをいただければ幸いです。

回答:


83

window使用する代わりにglobal

it('correct url is called', () => {
  global.open = jest.fn();
  statementService.openStatementsReport(111);
  expect(global.open).toBeCalled();
});

あなたも試すことができます

const open = jest.fn()
Object.defineProperty(window, 'open', open);

3
これを試しましたが、うまくいきませんでした。私の場合は奇妙で、モックはローカルで機能しますが、TravisでのPRマージでは機能しません...何か考えはありますか?
Alex JM

@AlexJM同じ問題がありますか?ウィンドウオブジェクトをどのようにモックするかを共有してください。
ダニー2017年

1
テストでwindow.propertyを定義するだけです
maracuja-juice 2018

@アンドレアスは未定義のstackoverflow.com/questions/59173156/

ありがとう!数時間後、私はただ変更windowする必要がありましたglobal
SrAxi

58

私のために働いた方法は次のとおりでした。このアプローチでは、に設定できるため、ブラウザとノードの両方で機能するコードをテストできましwindowundefined

これはJest24.8でした(私は信じています):

let windowSpy;

beforeEach(() => {
  windowSpy = jest.spyOn(window, "window", "get");
});

afterEach(() => {
  windowSpy.mockRestore();
});

it('should return https://example.com', () => {
  windowSpy.mockImplementation(() => ({
    location: {
      origin: "https://example.com"
    }
  }));

  expect(window.location.origin).toEqual("https://example.com");
});

it('should be undefined.', () => {
  windowSpy.mockImplementation(() => undefined);

  expect(window).toBeUndefined();
});

Object.definePropertyこれにより、モック時に他のテストに影響を与えないため、これははるかに優れています。
セルゲイ

10

を使用globalして定義することもできますsetupTests

// setupTests.js
global.open = jest.fn()

そしてglobal、実際のテストで使用してそれを呼び出します:

// yourtest.test.js
it('correct url is called', () => {
    statementService.openStatementsReport(111);
    expect(global.open).toBeCalled();
});

7

Jestでグローバルをモックする方法はいくつかあります。

  1. mockImplementationアプローチ(ほとんどのJestのような方法)を使用しますがjsdom、によって提供されるデフォルトの実装がある変数に対してのみ機能しwindow.openます。
test('it works', () => {
  // setup
  const mockedOpen = jest.fn();
  // without making a copy you will have a circular dependency problem
  const originalWindow = { ...window };
  const windowSpy = jest.spyOn(global, "window", "get");
  windowSpy.mockImplementation(() => ({
    ...originalWindow, // in case you need other window properties to be in place
    open: mockedOpen
  }));

  // tests
  statementService.openStatementsReport(111)
  expect(mockedOpen).toBeCalled();

  // cleanup
  windowSpy.mockRestore();
});
  1. グローバルプロパティに直接値を割り当てます。最も簡単ですが、一部のwindow変数でエラーメッセージが表示される場合がありますwindow.href
test('it works', () => {
  // setup
  const mockedOpen = jest.fn();
  const originalOpen = window.open;
  window.open = mockedOpen;

  // tests
  statementService.openStatementsReport(111)
  expect(mockedOpen).toBeCalled();

  // cleanup
  window.open = originalOpen;
});
  1. グローバルを直接使用しないでください(少しリファクタリングが必要です)

グローバル値を直接使用する代わりに、別のファイルからインポートする方がクリーンな場合があるため、Jestではモックが簡単になります。

./test.js

jest.mock('./fileWithGlobalValueExported.js');
import { windowOpen } from './fileWithGlobalValueExported.js';
import { statementService } from './testedFile.js';

// tests
test('it works', () => {
  statementService.openStatementsReport(111)
  expect(windowOpen).toBeCalled();
});

./fileWithGlobalValueExported.js

export const windowOpen = window.open;

./testedFile.js

import { windowOpen } from './fileWithGlobalValueExported.js';
export const statementService = {
  openStatementsReport(contactIds) {
    windowOpen(`a_url_${contactIds}`);
  }
}

5

あなたはこれを試すことができます:

import * as _Window from "jsdom/lib/jsdom/browser/Window";

window.open = jest.fn().mockImplementationOnce(() => {
    return new _Window({ parsingMode: "html" });
});

it("correct url is called", () => {
    statementService.openStatementsReport(111);
    expect(window.open).toHaveBeenCalled();
});


4

私はそれを行う簡単な方法を見つけました:削除して置き換える

describe('Test case', () => {
  const { open } = window;

  beforeAll(() => {
    // Delete the existing
    delete window.open;
    // Replace with the custom value
    window.open = jest.fn();
    // Works for `location` too, eg:
    // window.location = { origin: 'http://localhost:3100' };
  });

  afterAll(() => {
    // Restore original
    window.open = open;
  });

  it('correct url is called', () => {
    statementService.openStatementsReport(111);
    expect(window.open).toBeCalled(); // Happy happy, joy joy
  });
});

3

私のコンポーネントでは、アクセスする必要がありますwindow.location.search。これは、jestテストで行ったことです。

Object.defineProperty(global, "window", {
  value: {
    location: {
      search: "test"
    }
  }
});

ウィンドウのプロパティがテストごとに異なる必要がある場合は、ウィンドウのモックを関数に入れて、さまざまなテストをオーバーライドするために書き込み可能にすることができます。

function mockWindow(search, pathname) {
  Object.defineProperty(global, "window", {
    value: {
      location: {
        search,
        pathname
      }
    },
    writable: true
  });
}

そして、各テスト後にリセットします

afterEach(() => {
  delete global.window.location;
});

3

に直接割り当てjest.fn()ていwindow.openます。

window.open = jest.fn()
// ...code
expect(window.open).toHaveBeenCalledTimes(1)
expect(window.open).toHaveBeenCalledWith('/new-tab','__blank')

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.