我该如何编写一个测试,以确保方法reloadFn
实际上重新加载窗口?我找到了这个资源,但我不清楚在编写测试时如何期望窗口重新加载,尤其是当该窗口重新加载发生在给定函数中时。感谢您的帮助!
const reloadFn = () => {
window.location.reload();
}
我该如何编写一个测试,以确保方法reloadFn
实际上重新加载窗口?我找到了这个资源,但我不清楚在编写测试时如何期望窗口重新加载,尤其是当该窗口重新加载发生在给定函数中时。感谢您的帮助!
const reloadFn = () => {
window.location.reload();
}
包:
"jest": "^26.6.0"
"@testing-library/jest-dom": "^5.11.4"
构建:create-react-app 4
describe("test window location's reload function", () => {
const original = window.location;
const reloadFn = () => {
window.location.reload();
};
beforeAll(() => {
Object.defineProperty(window, 'location', {
configurable: true,
value: { reload: jest.fn() },
});
});
afterAll(() => {
Object.defineProperty(window, 'location', { configurable: true, value: original });
});
it('mocks reload function', () => {
expect(jest.isMockFunction(window.location.reload)).toBe(true);
});
it('calls reload function', () => {
reloadFn(); // as defined above..
expect(window.location.reload).toHaveBeenCalled();
});
});
这是解决方案,但为了更好的组织进行了重构:
describe('test window location\'s reload function', () => {
const { reload } = window.location;
beforeAll(() => {
Object.defineProperty(window.location, 'reload', {
configurable: true,
});
window.location.reload = jest.fn();
});
afterAll(() => {
window.location.reload = reload;
});
it('mocks reload function', () => {
expect(jest.isMockFunction(window.location.reload)).toBe(true);
});
it('calls reload function', () => {
reloadFn(); // as defined above..
expect(window.location.reload).toHaveBeenCalled();
});
});
谢谢 :)
window
的location
属性。reload
函数设置location
属性。const location: Location = window.location;
delete window.location;
window.location = {
...location,
reload: jest.fn()
};
// <code to test>
// <code to test>
// <code to test>
expect(window.location.reload).toHaveBeenCalledTimes(1);
jest.restoreAllMocks();
window.location = location;
TypeScript 4有更严格的检查(这是件好事),因此我不确定除了使用 @ts-ignore
或者 @ts-expect-error
来抑制错误以外是否还有其他方法。
警告: 抑制TypeScript验证可能会存在风险。
const location: Location = window.location;
// WARNING:
// @ts-ignore and @ts-expect-error suppress TypeScript validations by ignoring errors.
// Suppressing TypeScript validations can be dangerous.
// @ts-ignore
delete window.location;
window.location = {
...location,
reload: jest.fn()
};
// <code to test>
// <code to test>
// <code to test>
expect(window.location.reload).toHaveBeenCalledTimes(1);
jest.restoreAllMocks();
window.location = location;
window.location.reload
分配给写入。 - matt.kauffman23你也可以简化Murtaza Hussain的解决方案为
describe('refreshPage', () => {
const { reload } = window.location;
beforeAll(() => {
Object.defineProperty(window, 'location', {
writable: true,
value: { reload: jest.fn() },
});
});
afterAll(() => {
window.location.reload = reload;
});
it('reloads the window', () => {
refreshPage();
expect(window.location.reload).toHaveBeenCalled();
});
});
writable
而不是configurable
,对我来说比Murtaza的答案有效。 - Damian GreenrefreshPage
函数? - fredrivett您可以使用sessionStorage在每次重新加载页面时保存一个值。 只要浏览器不关闭,该值就会保留在sessionStorage中。 当页面重新加载时,该值将增加。使用此值验证新的重新加载。 通过将reloadFn()粘贴到控制台中进行测试。 控制台将显示“重新加载计数:1”,并随着每次重新加载而增加。
const reloadFn = () => {
window.location.reload(true);
}
window.onload = function() {
// get reloadCount from sessionStorage
reloadCount = sessionStorage.getItem('reloadCount');
// reloadCount will be null the first page load or a new value for each reload
if (reloadCount) {
// increment reloadCount
reloadCount = parseInt(reloadCount) + 1;
// save the new value to sessionStorage
sessionStorage.setItem('reloadCount', reloadCount);
console.log("Reload count: " + reloadCount);
} else {
// if reloadCount was null then set it to 1 and save to sessionStorage
sessionStorage.setItem('reloadCount', 1);
console.log("Page was loaded for the first time");
}
}
不必使用Object.defineProperty
的变通方法,您可以使用Jest的本地spyOn
函数来实现以下功能:
test("reload test", () => {
const { getByText } = renderComponentWithReloadButton()
const reload = jest.fn()
jest
.spyOn(window, "location", "get")
.mockImplementation(() => ({ reload } as unknown as Location))
// Call an action that should trigger window.location.reload() function
act(() => {
getByText("Reload me").click()
})
// Test if `reload` function was really called
expect(reload).toBeCalled()
})
同时,请确保在测试之后使用jest.clearAllMocks()
函数清除模拟。
在你的函数测试中,你应该使用你链接的模拟代码:reloadFn
。
Object.defineProperty(window.location, 'reload', {
configurable: true,
}); // makes window.location.reload writable
window.location.reload = jest.fn(); // set up the mock
reloadFn(); // this should call your mock defined above
expect(window.location.reload).toHaveBeenCalled(); // assert the call
window.location.reload.mockRestore(); // restore window.location.reload to its original function
为了进行更加完善的测试,您可以使用
expect(window.location.reload).toHaveBeenCalledWith(true);
mockRestore
仅适用于使用 spyOn
创建的模拟对象:https://jestjs.io/fr/docs/mock-function-api#mockfnmockrestore - SoullivaneuhonConfirmChange() {
const {data, id} = this.state;
this.setState({showConfirmationModal: false}, () => {
this.update(data, id)
.then(() => window.location.reload());
});
}
我的构建服务器使用的是chrome 71版本,最初测试失败了,但在我本地使用chrome 79版本时通过了测试。 今天我将chrome更新到了84版本,在我的本地环境中出现了问题。 删除window.local似乎不被支持。尝试了谷歌上找到的所有解决方案,但都没有奏效。
那么解决方案是什么呢?
实际上非常简单,对于react测试,我的系统使用enzyme,所以我将window.location.reload
包装在一个实例方法中,并在测试中进行存根处理。
JSX代码:
reloadWindow() {
window.location.reload();
}
onConfirmChange() {
const {data, id} = this.state;
this.setState({showConfirmationModal: false}, () => {
this.update(data, id)
.then(() => reloadWindow());
});
}
测试
it('check what happened', () => {
render();
const component = wrapper.instance();
sandbox.stub(component, 'reloadWindow').callsFake();
});
我在这个帖子中遇到了很多问题。我认为随着时间推移,底层库的版本变化导致了故障。
我的配置:
"typescript": "~4.1.5"
"jest": "^26.6.3"
"jest-environment-jsdom": "^26.6.2"
另外,我应该注意到,我的解决方案非常冗长。但是我的用例需要测试 window.location.replace()
和结果。所以我不能简单地模拟 window.location.replace
。如果您只需要模拟其中一个函数并且不关心实际的 href
如何更改,则该线程中的某些解决方案将使用更少的代码非常好用。
我发现完全填充 window.location
对象可以解决我所有的问题。
window.location
填充使用此代码并将其放置在测试文件或设置文件中的任何位置:
export class MockWindowLocation {
private _url: URL = new URL();
get href (): string {
return this._url.toString();
}
set href (url: string) {
this._url = new URL(url);
}
get protocol (): string {
return this._url.protocol;
}
get host (): string {
return this._url.host;
}
get hostname (): string {
return this._url.hostname;
}
get origin (): string {
return this._url.origin;
}
get port (): string {
return this._url.port;
}
get pathname (): string {
return this._url.pathname;
}
get hash (): string {
return this._url.hash;
}
get search (): string {
return this._url.search;
}
replace = jest.fn().mockImplementation((url: string) => {
this.href = url;
});
assign = jest.fn().mockImplementation((url: string) => {
this.href = url;
});
reload = jest.fn();
toString(): string {
return this._url.toString();
}
}
然后,您必须删除 window.location
并将其设置为新的 polyfill:
it('should be able to test window.location', () => {
delete window.location;
Object.defineProperty(window, 'location', {
value: new MockWindowLocation()
});
window.location.href = 'https://example.com/app/#/route/1';
window.location.reload();
expect(window.location.reload).toHaveBeenCalled();
expect(window.location.href).toBe('https://example.com/app/#/route/1');
expect(window.location.pathname).toBe('/app/');
expect(window.location.hash).toBe('#/route/1');
});
这对我非常有效,希望能帮助其他人。
再强调一遍,这个主题中还有其他完全可行的答案。我发现:
Object.defineProperty(window, 'location', {
writable: true,
value: { reload: jest.fn() },
});
而且:
const location: Location = window.location;
delete window.location;
window.location = {
...location,
reload: jest.fn()
};
两者都很有帮助。但是像我说的,我需要窥探replace()
并仍然保留window.location
的标准功能。
希望这能帮助到某些人。干杯!
expect(spy).to.have.been.called.exactly(3);
- Matt Pengelly