我需要测试一个在浏览器中打开一个新选项卡的功能
openStatementsReport(contactIds) {
window.open(`a_url_${contactIds}`);
}
我想模拟窗口的打开函数,这样我就可以验证正确的URL被传递给打开函数。
使用笑话,我不知道如何模拟窗口。我试着设置窗口。使用mock函数打开,但这种方式不起作用。下面是测试用例:
it('the 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]
我应该对测试用例做什么?
下面的方法对我很有效。这种方法允许我测试一些在浏览器和Node.js中都可以工作的代码,因为它允许我将window设置为undefined。
这是笑话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();
});
在Jest中有几种方法可以模拟全局变量:
Use the mockImplementation approach (the most Jest-like way), but it will work only for those variables which has some default implementation provided by jsdom. window.open is one of them:
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();
});
Assign the value directly to the global property. It is the most straightforward, but it may trigger error messages for some window variables, e.g. 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;
});
Don't use globals directly (requires a bit of refactoring)
Instead of using the global value directly, it might be cleaner to import it from another file, so mocking will became trivial with Jest.
文件。/ . 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}`);
}
}
在我的组件,我需要访问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;
});
我有一个实用函数,它允许我模拟窗口上的任何方法,如下所示:
function givenMockWindowMethods(methods: Partial<{ [key in keyof Window]: jest.Mock<any, any> }>): () => void {
const mocks = Object.values(methods);
Object.entries(methods).forEach(([key, value]) => {
Object.defineProperty(window, key, { value });
});
return (): void => mocks.forEach((mock) => mock?.mockClear());
}
因此,如果我需要模拟窗口上的open方法(或任何东西),我可以这样做:
const cleanupMocks = givenMockWindowMethods({ open: jest.fn() });
// expect(...).toBe(...)
//at the end of the test, clean it up
cleanupMocks()
我试过一个类似的测试,对我很有效……
我的代码:
export const Blah = () => {
const BLAH = 'https://www.google.com/'
const handleBlah = () => {
window.open(BLAH, '_blank')
}
return (
<button onClick={handleBlah}> BLAHBLAH </button>
)
}
我的测试使用Jest:
it('should be able to render "BLAHBLAH " button ', () => {
window.open = jest.fn();
const BLAH = 'https://www.google.com/'
const { getByText } = render(<Blah/>) // Get text by my page Blah
const buttonGoToBlah = getByText('BLAHBLAH') // Get button by text
fireEvent.click(buttonGoToBlah) // Simulate the click event
expect(window.open).toHaveBeenCalledTimes(1) // Expect the window.open have to been called at least once.
expect(window.open).toHaveBeenCalledWith(BLAH, '_blank'); // And the page should be the same called in my BLAH page
})