使用jest.js拦截导航更改(或如何覆盖和恢复location.href)

k4ymrczo  于 2023-01-28  发布在  Jest
关注(0)|答案(4)|浏览(193)

应用程序代码正在调用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
有没有更好的方法来拦截导航事件以便对其进行测试?
谢谢

tgabmvqs

tgabmvqs1#

使用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
});
cxfofazt

cxfofazt2#

较新的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/

rggaifut

rggaifut3#

正如quotesBro已经解释过的in his answer,您应该使用location.assign()
但是从Jest v25(使用较新版本的JSDOM)开始,您将得到以下错误:

TypeError: Cannot assign to read only property 'assign' of object '[object Location]'

顺便说一句,这不是Jest/JSDOM的bug。这是正常的浏览器行为,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 = { ...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')
  })
})
2jcobegt

2jcobegt4#

另一个非常简单的解决方案是在测试文件的开头模拟locationwindow.location对象

const assignSpy = jest.fn();

Object.defineProperty(window, 'location', {
  value: { assign: assignSpy }
});

describe('My awesome unit test', () => {
it('should call location.assign', () => {    
    // ...your test code

    expect(window.location.assign).toHaveBeenCalled()

    // or even better:
    // expect(window.location.assign).toHaveBeenCalledWith('/my_link')
 })
}

相关问题