Як знущатися над об’єктом вікна JavaScript за допомогою Jest?


112

Мені потрібно протестувати функцію, яка відкриває нову вкладку в браузері

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

Я хотів би знущатися над openфункцією вікна, щоб я міг перевірити, чи правильно передано URL-адресу openфункції.

Використовуючи Jest, я не знаю, як знущатися над window. Я намагався встановити window.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]

Що робити з тестом? Будь-які пропозиції та підказки будуть вдячні.

Відповіді:


91

Замість 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
Спробував це, але не працює для мене. Мій випадок дивний, насмішки працюють локально, але не для злиття PR у Тревісі ... будь-яка ідея?
Alex JM

@AlexJM у вас така сама проблема? розум поділитися, як ви знущаєтесь над об'єктом вікна?
danny

1
Я просто визначаю window.property у своїх тестах
maracuja-juice

@ Andreas є спосіб , щоб дражнити вікно як невизначена stackoverflow.com/questions/59173156 / ...
Діліп THOMAS

Дякую! Після декількох годин, мені просто потрібно , щоб зміни windowдляglobal
SrAxi

70

Метод, який працював у мене, був наступним. Цей підхід дозволив мені протестувати деякий код, який повинен працювати як у браузері, так і в Node, оскільки він дозволив мені встановити windowзначення undefined.

Це було з Jest 24.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();
});

1
Це набагато краще, ніж Object.definePropertyоскільки це дозволяє не впливати на інші тести при знущанні.
Сергій

Це повинна бути прийнята відповідь, оскільки вона глузує / підглядає, а не змінює фактичне глобальне властивість
user2490003

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

7

Я знайшов простий спосіб зробити це: видалити та замінити

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

7

У моєму компоненті, до якого мені потрібен доступ window.location.search, це те, що я зробив у тесті на жарт:

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

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

Я безпосередньо призначаючи jest.fn()до window.open.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.