Muszę przetestować funkcję, która otwiera nową kartę w przeglądarce
openStatementsReport(contactIds) {
window.open(`a_url_${contactIds}`);
}
Chciałbym sfałszować funkcję okna, open
aby sprawdzić, czy do open
funkcji jest przekazywany poprawny adres URL .
Używając Jest, nie wiem, jak kpić z window
. Próbowałem ustawić window.open
za pomocą funkcji pozorowanej, ale ten sposób nie działa. Poniżej znajduje się przypadek testowy
it('correct url is called', () => {
window.open = jest.fn();
statementService.openStatementsReport(111);
expect(window.open).toBeCalled();
});
ale daje mi błąd
expect(jest.fn())[.not].toBeCalled()
jest.fn() value must be a mock function or spy.
Received:
function: [Function anonymous]
Co powinienem zrobić z przypadkiem testowym? Wszelkie sugestie i wskazówki są mile widziane.
javascript
mocking
jestjs
Danny
źródło
źródło
window
naglobal
Metoda, która zadziałała w moim przypadku, była następująca. Takie podejście pozwoliło mi do testowania kodu, który powinien pracować zarówno w przeglądarce, a w węźle, ponieważ pozwoliło mi ustawić
window
sięundefined
.Tak było z Jest 24.8 (wierzę):
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(); });
źródło
Object.defineProperty
ponieważ pozwala to nie wpływać na inne testy podczas kpiny.Możemy to również zdefiniować za pomocą
global
wsetupTests
// setupTests.js global.open = jest.fn()
I nazwij to używając
global
w rzeczywistym teście:// yourtest.test.js it('correct url is called', () => { statementService.openStatementsReport(111); expect(global.open).toBeCalled(); });
źródło
Istnieje kilka sposobów na udawanie globali w Jest:
mockImplementation
podejścia (większość jest jak sposób), ale zadziała tylko dla tych zmiennych, które mają pewną domyślną implementację dostarczoną przezjsdom
,window.open
jest jedną z nich: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(); });
window
zmiennych, npwindow.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; });
Zamiast używać wartości globalnej bezpośrednio, może być czystsze importowanie jej z innego pliku, więc mockowanie stanie się trywialne w przypadku 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}`); } }
źródło
Znalazłem na to łatwy sposób: usuń i zamień
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 }); });
źródło
W moim komponencie, do którego potrzebuję dostępu
window.location.search
, oto co zrobiłem w żartobliwym teście:Object.defineProperty(global, "window", { value: { location: { search: "test" } } });
W przypadku, gdy właściwości okna muszą być różne w różnych testach, możemy umieścić mockowanie okna w funkcji i uczynić ją zapisywalną w celu nadpisania dla różnych testów:
function mockWindow(search, pathname) { Object.defineProperty(global, "window", { value: { location: { search, pathname } }, writable: true }); }
I resetuj po każdym teście
afterEach(() => { delete global.window.location; });
źródło
Możesz spróbować tego:
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(); });
źródło
Jeśli jest podobny do problemu z lokalizacją okna na https://github.com/facebook/jest/issues/890 , możesz spróbować [dostosowany]
delete global.window.open; global.window = Object.create(window); global.window.open = jest.fn();
źródło
Przypisuję bezpośrednio
jest.fn()
dowindow.open
.window.open = jest.fn() // ...code expect(window.open).toHaveBeenCalledTimes(1) expect(window.open).toHaveBeenCalledWith('/new-tab','__blank')
źródło
W swojej konfiguracji jest dodaj setupFilesAfterEnv: ["./setupTests.js"], utwórz ten plik i dodaj kod, który chcesz uruchomić przed testami
//setupTests.js window.crypto = { ..... };
Ref: https://jestjs.io/docs/en/configuration#setupfilesafterenv-array
źródło