Wie verspotte ich das JavaScript-Fensterobjekt mit Jest?

90

Ich muss eine Funktion testen, die einen neuen Tab im Browser öffnet

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

Ich möchte die Öffnungsfunktion des Fensters verspotten, damit ich überprüfen kann, ob die richtige URL an die Öffnungsfunktion übergeben wird.

Mit Jest weiß ich nicht, wie ich das Fenster verspotten soll. Ich habe versucht, window.open mit einer Mock-Funktion einzustellen, aber dieser Weg funktioniert nicht. Unten ist der Testfall

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

aber es gibt mir den Fehler

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

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

Was soll ich mit dem Testfall machen? Anregungen oder Hinweise sind willkommen

danny
quelle

Antworten:

75

Anstelle von windowGebrauchglobal

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

Sie könnten es auch versuchen

const open = jest.fn()
Object.defineProperty(window, 'open', open);
Andreas Köberle
quelle
3
Versuchte dies, arbeitete aber nicht für mich. Mein Fall ist seltsam, Spott funktioniert lokal, aber nicht für eine PR-Fusion in Travis ... eine Idee?
Alex JM
@AlexJM hast du das gleiche Problem? Möchten Sie mitteilen, wie Sie das Fensterobjekt verspotten?
Danny
1
Ich definiere nur window.property in meinen Tests
Maracuja-Saft
@ Andreas gibt es eine Möglichkeit, das Fenster als undefiniert zu verspotten stackoverflow.com/questions/59173156/…
DILEEP THOMAS
Vielen Dank! Nach Stunden, die ich ändern muß nur windowfürglobal
SrAxi
44

Eine Methode, die für mich funktioniert hat, war die folgende. Dieser Ansatz erlaubt mir einige Code zu testen , die sowohl im Browser und in Knoten funktionieren sollte, da es mir erlaubt, eingestellt windowzu undefined.

Dies war mit Jest 24.8 (glaube ich):

let windowSpy;

beforeEach(() => {
  windowSpy = jest.spyOn(global, '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();
});
tvsbrent
quelle
Das ist viel besser als Object.definePropertyda dies erlaubt, andere Tests beim Verspotten nicht zu beeinflussen.
Sergey
10

Wir können es auch mit globalin definierensetupTests

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

Und nennen Sie es globalim eigentlichen Test:

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

Sie können dies versuchen:

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();
});
abhishek khandait
quelle
5

Es gibt verschiedene Möglichkeiten, sich in Jest über Globals lustig zu machen:

  1. Verwenden Sie den mockImplementationAnsatz (meistens scherzhaft), aber er funktioniert nur für die Variablen, für die eine Standardimplementierung bereitgestellt wird. jsdomDies window.openist eine davon:
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. Weisen Sie der globalen Eigenschaft einen Wert zu, der am einfachsten ist, aber bei einigen windowVariablen, z 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. Verwenden Sie Globals nicht direkt (erfordert ein wenig Refactoring)

Anstatt den globalen Wert direkt zu verwenden, ist es möglicherweise sauberer, ihn aus einer anderen Datei zu importieren, sodass das Verspotten mit Jest trivial wird.

./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}`);
  }
}
jmarceli
quelle
3

Wenn es dem Problem mit der Fensterposition unter https://github.com/facebook/jest/issues/890 ähnelt , können Sie versuchen, [angepasst]

delete global.window.open;
global.window = Object.create(window);
global.window.open = jest.fn();
serv-inc
quelle
3

Ich habe einen einfachen Weg gefunden: Löschen und Ersetzen

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
  });
});
Jee Mok
quelle
2

Fügen Sie in Ihrer Scherzkonfiguration setupFilesAfterEnv: ["./setupTests.js"] hinzu, erstellen Sie diese Datei und fügen Sie den Code hinzu, den Sie vor den Tests ausführen möchten

//setupTests.js
window.crypto = {
   .....
};

Ref: https://jestjs.io/docs/en/configuration#setupfilesafterenv-array

stefan
quelle
2

In meiner Komponente, auf die ich Zugriff habe window.location.search, habe ich Folgendes im Scherztest getan:

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

Falls die Fenstereigenschaften in verschiedenen Tests unterschiedlich sein müssen, können wir eine Verspottung in eine Funktion einfügen und diese beschreibbar machen, um sie für verschiedene Tests zu überschreiben:

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

Und nach jedem Test zurücksetzen

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