目前,我正在为我的项目实施单元测试,并且有一个文件包含
window.location.href
。
我想模拟这个来测试,这是我的示例代码:
it("method A should work correctly", () => {
const url = "http://dummy.com";
Object.defineProperty(window.location, "href", {
value: url,
writable: true
});
const data = {
id: "123",
name: null
};
window.location.href = url;
wrapper.vm.methodA(data);
expect(window.location.href).toEqual(url);
});
但我收到此错误:
TypeError: Cannot redefine property: href
at Function.defineProperty (<anonymous>)
我该如何解决?
你可以尝试:
window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, 'location', {
value: {
href: url
},
writable: true // possibility to override
});
expect(window.location.href).toEqual(url);
查看该问题的 Jest Issue:
笑话问题
基本
URL 对象 具有许多与 Location 对象 相同的功能。换句话说,它包括诸如
pathname
、search
、hostname
等属性。因此,对于大多数情况,您可以执行以下操作:
delete window.location
window.location = new URL('https://www.example.com')
高级
您还可以模拟您可能需要的位置方法,这些方法在 URL 接口上不存在:
const location = new URL('https://www.example.com')
location.assign = jest.fn()
location.replace = jest.fn()
location.reload = jest.fn()
delete window.location
window.location = location
我通过添加
writable: true
并将其移至 beforeEach
解决了这个问题
这是我的示例代码:
global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, "location", {
value: {
href: url
},
writable: true
});
2019 年解决方案来自 GitHub:
delete global.window.location; global.window = Object.create(window); global.window.location = { port: '123', protocol: 'http:', hostname: 'localhost', };
URL
实例,以便它像 location.href
一样解析字符串,并更新 location
的所有属性,例如 .hash
、.search
、.protocol
等等
it("method A should work correctly", () => {
const url = "http://dummy.com/";
Object.defineProperty(window, "location", {
value: new URL(url)
} );
window.location.href = url;
expect(window.location.href).toEqual(url);
window.location.href += "#bar"
expect(window.location.hash).toEqual("#bar");
});
2020 年使用
@testing-library/react
进行 window.location.assign 的工作示例:
afterEach(cleanup)
beforeEach(() => {
Object.defineProperty(window, 'location', {
writable: true,
value: { assign: jest.fn() }
})
})
提供的许多示例并未模拟原始 Location 对象的属性。
我所做的只是用 URL 替换 Location 对象(window.location),因为 URL 包含与 Location 对象相同的属性,如“href”、“search”、“hash”、“host”。
Setter 和 Getters 的工作方式与 Location 对象完全相同。
示例:
const realLocation = window.location;
describe('My test', () => {
afterEach(() => {
window.location = realLocation;
});
test('My test func', () => {
// @ts-ignore
delete window.location;
// @ts-ignore
window.location = new URL('http://google.com');
console.log(window.location.href);
// ...
});
});
扩展@jabacchetta 的解决方案以避免此设置渗透到其他测试中:
describe("Example", () => {
let location;
beforeEach(() => {
const url = "https://example.com";
location = window.location;
const mockLocation = new URL(url);
mockLocation.replace = jest.fn();
delete window.location;
window.location = mockLocation;
});
afterEach(() => {
window.location = location;
});
});
你可以尝试jest-location-mock。
npm install --save-dev jest-location-mock
更新
jest
文件中的jest.config.js
配置或jest
内的package.json
道具:
setupFilesAfterEnv: [ "./config/jest-setup.js" ]
创建
jest-setup.js
import "jest-location-mock";
用途:
it("should call assign with a relative url", () => {
window.location.assign("/relative-url");
expect(window.location).not.toBeAt("/");
expect(window.location).toBeAt("/relative-url");
});
2022 年 TS
const orgLocation: Location = window.location;
beforeEach(() => {
delete (window as any).location;
window.location = (new URL("http://localhost") as any);
window.location.assign = ({configurable:true, value:jest.fn()} as any);
window.location.replace = ({configurable:true, value:jest.fn()} as any);
window.location.reload = ({configurable:true, value:jest.fn()} as any);
});
afterAll(() => window.location = orgLocation);
如何在代码库中重新分配 window.location;我们为 Jest 测试找到的最简单的工作设置:
const realLocation = window.location;
beforeEach(() => {
delete window.location;
});
afterEach(() => {
window.location = realLocation;
});
你可以尝试一个帮手:
const setURL = url => global.jsdom.reconfigure({url});
describe('Test current location', () => {
test('with GET parameter', () => {
setURL('https://test.com?foo=bar');
// ...your test here
});
});
jest.spyOn()
)而不是直接覆盖对象属性。
describe("...", () => {
beforeEach(() => {
const originalLocation = window.location;
jest.spyOn(window, "location", "get").mockImplementation(() => ({
...originalLocation,
href: "http://dummy.com", // Mock window.location.href here.
}))
});
afterEach(() => {
jest.restoreAllMocks()
});
it("...", () => {
// ...
})
});
我从这篇文章中学到了它。
这对于 Jest + TypeScript + Next.js 有效(如果您使用
useRoute().push
const oldWindowLocation = window.location;
beforeAll(() => {
delete window.location;
window.location = { ...oldWindowLocation, assign: jest.fn() };
});
afterAll(() => {
window.location = oldWindowLocation;
});
JSDOM 版本
另一种方法,使用 JSDOM,它将提供
window.location.href
以及 window.location
的所有其他属性(例如 window.location.search
用于获取查询字符串参数)。
import { JSDOM } from 'jsdom';
...
const { window } = new JSDOM('', {
url: 'https://localhost/?testParam=true'
});
delete global.window;
global.window = Object.create(window);
我找不到如何测试
window.location.href
是否已设置正确的值并测试 window.location.replace()
是否已使用正确的参数调用,但我尝试了这个,它看起来很完美。
const mockWindowLocationReplace = jest.fn()
const mockWindowLocationHref = jest.fn()
const mockWindowLocation = {}
Object.defineProperties(mockWindowLocation, {
replace: {
value: mockWindowLocationReplace,
writable: false
},
href : {
set: mockWindowLocationHref
}
})
jest.spyOn(window, "location", "get").mockReturnValue(mockWindowLocation as Location)
describe("my test suite", () => {
// ...
expect(mockWindowLocationReplace).toHaveBeenCalledWith('foo')
expect(mockWindowLocationHref).toHaveBeenCalledWith('bar')
})
可以通过在每次测试中删除此全局来重写 window.location。
delete global.window.location;
const href = 'http://localhost:3000';
global.window.location = { href };
基于上面和其他线程中的示例,这里是一个使用
jest
的具体示例,可能会对某人有所帮助:
describe('Location tests', () => {
const originalLocation = window.location;
const mockWindowLocation = (newLocation) => {
delete window.location;
window.location = newLocation;
};
const setLocation = (path) =>
mockWindowLocation(
new URL(`https://example.com${path}`)
);
afterEach(() => {
// Restore window.location to not destroy other tests
mockWindowLocation(originalLocation);
});
it('should mock window.location successfully', () => {
setLocation('/private-path');
expect(window.location.href).toEqual(
`https://example.com/private-path`
);
});
});
可能无关紧要。但对于那些寻求 window.open('url', attribute) 解决方案的人来说,我在上面的一些评论的帮助下应用了这个:
window = Object.create(window);
const url = 'https://www.9gag.com';
Object.defineProperty(window, 'open', { value: url });
expect(window.open).toEqual(url);
这是一个简单的,您可以在每次测试中使用
beforeEach
或点菜。
它利用 Javascript
window.history
及其 pushState
方法来操作 URL。
window.history.pushState({}, 'Enter Page Title Here', '/test-page.html?query=value');
简单干净的方法:
Object.defineProperty(window, "location", {
value: new URL("http://example.com"),
configurable: true,
});