Làm thế nào để giả lập đối tượng cửa sổ JavaScript bằng Jest?


104

Tôi cần kiểm tra một chức năng mở tab mới trong trình duyệt

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

Tôi muốn mô phỏng openchức năng của cửa sổ để tôi có thể xác minh đúng URL được chuyển vào openhàm.

Sử dụng Jest, tôi không biết làm thế nào để chế nhạo window. Tôi đã cố gắng đặt window.openbằng một hàm giả nhưng cách này không hiệu quả. Dưới đây là trường hợp thử nghiệm

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

nhưng nó cho tôi lỗi

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

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

Tôi nên làm gì với trường hợp thử nghiệm? Bất kỳ đề xuất hoặc gợi ý được đánh giá cao.

Câu trả lời:


83

Thay vì windowsử dụngglobal

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

bạn cũng có thể thử

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

3
Đã thử điều này nhưng không hiệu quả với tôi. Trường hợp của tôi thật kỳ lạ, chế giễu hoạt động cục bộ nhưng không hợp nhất với PR trong Travis ... bạn có ý kiến ​​gì không?
Alex JM

@AlexJM bạn có vấn đề tương tự không? tâm trí để chia sẻ làm thế nào để bạn chế nhạo đối tượng cửa sổ?
danny

1
Tôi chỉ xác định window.property trong các thử nghiệm của tôi
Maracuja-nước

@ Andreas có cách nào để chế nhạo cửa sổ dưới dạng stackoverflow.com/questions/59173156/…
DILEEP THOMAS

Cảm ơn! Sau giờ làm việc, tôi chỉ cần thay đồ windowchoglobal
SrAxi

58

Một phương pháp đã làm việc cho tôi là sau đây. Cách tiếp cận này cho phép tôi kiểm tra một số mã sẽ hoạt động cả trong trình duyệt và trong Node, vì nó cho phép tôi đặt windowthành undefined.

Đây là với Jest 24.8 (tôi tin):

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();
});

Điều đó tốt hơn nhiều so với Object.definePropertyđiều này cho phép không ảnh hưởng đến các thử nghiệm khác khi chế nhạo.
Sergey

10

Chúng tôi cũng có thể xác định nó bằng cách sử dụng globaltrongsetupTests

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

Và gọi nó bằng cách sử dụng globaltrong thử nghiệm thực tế:

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

7

Có một số cách để chế nhạo các quả cầu trong Jest:

  1. Sử dụng mockImplementationphương pháp (hầu hết đùa như chiều), nhưng nó sẽ làm việc chỉ dành cho những biến trong đó có một số cài đặt mặc định được cung cấp bởi jsdom, window.openlà một trong số họ:
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. Gán giá trị trực tiếp cho thuộc tính toàn cục, hầu hết đều thẳng về phía trước nhưng có thể kích hoạt thông báo lỗi cho một số windowbiến, ví dụ 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. Không sử dụng trực tiếp hình cầu (yêu cầu cấu trúc lại một chút)

Thay vì sử dụng trực tiếp giá trị toàn cục, có thể nhập nó từ một tệp khác sẽ gọn gàng hơn, vì vậy việc chế nhạo sẽ trở nên tầm thường với 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

Bạn có thể thử điều này:

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

Tôi đã tìm thấy một cách dễ dàng để làm điều đó: xóa và thay thế

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

Trong thành phần của tôi, tôi cần quyền truy cập window.location.search, đây là những gì tôi đã làm trong thử nghiệm jest:

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

Trong trường hợp các thuộc tính cửa sổ phải khác nhau trong các thử nghiệm khác nhau, chúng ta có thể đặt tính năng giả lập cửa sổ vào một hàm và làm cho nó có thể ghi để ghi đè cho các thử nghiệm khác nhau:

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

Và đặt lại sau mỗi lần kiểm tra

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

3

Tôi đang trực tiếp giao jest.fn()cho window.open.

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.