如何用 Jest + Vuejs 模拟 window.location.href?

目前,我正在为我的项目实现单元测试,有一个包含 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>)

我尝试了一些解决方案,但没有解决它。我需要一些提示来帮助我摆脱这个麻烦。请帮帮我。

173219 次浏览

你可以试试:

global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, 'location', {
value: {
href: url
}
});
expect(window.location.href).toEqual(url);

看看这个问题的笑话:
开个玩笑

我已经通过添加 writable: true并将其移动到 beforeEach来解决这个问题

下面是我的示例代码:

global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, "location", {
value: {
href: url
},
writable: true
});

可以通过在每个测试中删除这个全局变量来重写 window.location。

delete global.window.location;
const href = 'http://localhost:3000';
global.window.location = { href };

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");
});

Https://repl.it/repls/voluminoushauntingfunctions

提供的许多示例都没有模仿原始 Location 对象的属性。

我所做的只是将 Location 对象(window.Location)替换为 URL,因为 URL 包含与 Location 对象相同的属性,如“ href”、“ search”、“ hash”、“ host”。

Setters 和 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);


// ...
});
});

2020年最新情况


基本的

URL 对象有很多与 位置对象相同的功能。换句话说,它包括诸如 pathnamesearchhostname等属性。因此,在大多数情况下,您可以执行以下操作:

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

基于上面的例子和其他线程,下面是一个使用 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`
);
});
});

扩展@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;
});
});

你可以试试这个助手:

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 + TypeScript + Next.js 是有效的(如果您使用 useRoute().push的话)

const oldWindowLocation = window.location;


beforeAll(() => {
delete window.location;
window.location = { ...oldWindowLocation, assign: jest.fn() };
});


afterAll(() => {
window.location = oldWindowLocation;
});

在2020年使用 @testing-library/react为 window.location.sign 的工作示例:

  afterEach(cleanup)
beforeEach(() => {
Object.defineProperty(window, 'location', {
writable: true,
value: { assign: jest.fn() }
})
})

JDOM 版本

另一种方法是使用 JDOM,它将提供 window.location.hrefwindow.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.open (‘ url’,属性)解决方案的人,我应用了这个方法,并借助上面的一些评论:

window = Object.create(window);
const url = 'https://www.9gag.com';
Object.defineProperty(window, 'open', { value: url });


expect(window.open).toEqual(url);

如何重新分配代码库中的 window.location; 我们为 Jest 测试找到的最简单的工作设置:

const realLocation = window.location;


beforeEach(() => {
delete window.location;
});


afterEach(() => {
window.location = realLocation;
});

你可以试试 玩笑-地点-模仿

npm install --save-dev jest-location-mock

更新 jest配置在 jest.config.js文件或 package.json内部的 jest道具:

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");
});

这里有一个简单的方法,你可以在每个测试的 beforeEach或者 ala 菜单中使用。

它利用 Javascript window.history及其 pushState方法来操作 URL。

window.history.pushState({}, 'Enter Page Title Here', '/test-page.html?query=value');

我使用以下方法使用 Jest 的嘲讽机制(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("...", () => {
// ...
})
});

我从 这篇文章学来的。

我找不到如何测试 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')
})