应用程序代码正在调用location.href = "some-url"
。
我想编写一个测试来验证导航重定向是否已经发生。
在jsdom上使用jest,我尝试使用jest mock函数重写location.href setter并且它正在工作。
但是现在我似乎无法在测试清理时恢复location.href属性,并且它在“location.href”上继续传输的其余测试失败。
it('test navigation happened', () => {
const originalLocationHref = Object.getOwnPropertyDescriptor(window.location, 'href'); // returns undefined
spyFn = jest.fn();
Object.defineProperty(window.location, 'href', {
set: spyFn,
enumerable: true,
configurable: true
});
someAppCodeThatShouldRedirectToSomeUrl();
expect(spyFn).toBeCalledWith('http://some-url'); // this is working
// Cleanup code, not working, because originalLocationHref is undefined
Object.defineProperty(window.location, 'href', originalLocationHref);
});
我错过了什么?为什么Object.getOwnPropertyDescriptor(window.location, 'href');
是undefined
?
是否有更好的方法来拦截导航事件以进行测试?
由于
答案 0 :(得分:14)
使用location.assign()方法代替将新的位置字符串分配给location.href。然后你可以毫无问题地模拟和测试它:
it('test navigation happened', () => {
window.location.assign = jest.fn();
// here you call location.assign('http://some-url');
redirectToSomeUrl();
expect(window.location.assign).toBeCalledWith('http://some-url');
// location.href hasn't changed because location.assign was mocked
});
答案 1 :(得分:11)
较新的jest / jsdom版本不允许再设置window.location.assign
。可以这样修复:
delete window.location;
window.location = { assign: jest.fn() };
请注意,这会从window.location中删除所有其他对象,您可能需要根据测试和应用程序代码来模拟更多的对象。
来源:https://remarkablemark.org/blog/2018/11/17/mock-window-location/
答案 2 :(得分:5)
正如quotesBro已解释in his answer一样,您应该使用location.assign()。
但是由于Jest v25(使用较新版本的JSDOM),您将收到以下错误:
TypeError: Cannot assign to read only property 'assign' of object '[object Location]'
这不是一个Jest / JSDOM错误。这是正常的浏览器行为,并且JSDOM会尝试像真实的浏览器一样工作。
一种解决方法是删除位置对象,创建自己的位置对象,并在运行测试后将其重置为原始位置对象:
describe('My awesome unit test', () => {
// we need to save the original object for later to not affect tests from other files
const realLocation = window.location
beforeAll(() => {
delete window.location
window.location = { assign: jest.fn() }
// or even like this if you are also using other location properties (or if Typescript complains):
// window.location = { ...realLocation, assign: jest.fn() }
})
afterAll(() => {
window.location = realLocation
})
it('should call location.assign', () => {
// ...your test code
expect(window.location.assign).toHaveBeenCalled()
// or even better:
// expect(window.location.assign).toHaveBeenCalledWith('/my_link')
})
})