【问题标题】:How to mock window.location.href with Jest + Vuejs?如何用 Jest + Vuejs 模拟 window.location.href?
【发布时间】:2019-05-29 23:14:57
【问题描述】:

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

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

【问题讨论】:

  • 这个问题已经在另一个帖子Here is the link to the answer得到了回答
  • 不完全是你要找的东西,但 window.location.assign(url) 在功能上做了同样的事情,所以你可以模拟它而不是使用 jest.spyOn(window.location, 'assign').mockImplementation(() =&gt; {});

标签: vue.js unit-testing jestjs jasmine vue-test-utils


【解决方案1】:

你可以试试:

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

查看该问题的 Jest Issue:
Jest Issue

【讨论】:

  • 对我来说使用global 不起作用,我删除了global.,我还需要添加writable: true 否则一旦设置了其他测试就无法更改它。
【解决方案2】:

2020 年更新


基本

URL object 有很多与Location object 相同的功能。换句话说,它包含pathnamesearchhostname等属性。所以对于大多数情况,您可以执行以下操作:

delete window.location
window.location = new URL('https://www.example.com')

高级

您还可以模拟您可能需要的Location methods,它在 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

【讨论】:

  • 随着打字稿更新,delete window.location 将触发错误The operand of a 'delete' operator must be optional
  • @dhsun 您上面提到的问题有什么解决方案吗?我面临着类似的问题
  • 添加 // @ts-ignore 删除 window.location;如果你必须
  • Reflect.deleteProperty(global.window, 'location') 处理没有 ts 错误
  • 要解决该 TS 错误,您可以执行 delete (window as any).location;
【解决方案3】:

我已通过添加writable: true 并将其移至beforeEach 来解决此问题

这是我的示例代码:

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

【讨论】:

  • 此解决方案也适用于在 Jest 测试中覆盖 window.location.hostname。我需要writable: true 才能多次更改hostname
  • 这对我很有帮助
  • 这就是我要找的。对我来说最好的解决方案。
【解决方案4】:

2019年解决方案from GitHub

delete global.window.location;
global.window = Object.create(window);
global.window.location = {
  port: '123',
  protocol: 'http:',
  hostname: 'localhost',
};

【讨论】:

  • 这是唯一一个似乎对我有用并且有帮助的理由! :)
  • 它可以工作,但是使用需要原始location.host 属性的 postMessage 会破坏以下测试
【解决方案5】:

最好的方法可能是创建一个新的URL 实例,以便它像location.href 那样解析您的字符串,并更新location 的所有属性,例如.hash.search、@987654328 @等

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

【讨论】:

    【解决方案6】:

    提供的许多示例并未模拟原始 Location 对象的属性。

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

    Setter 和 Getter 的工作方式也与 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);
    
            // ...
        });
    });
    

    【讨论】:

    • 我想知道它是否不应该是const realLocation = Object.assign({}, window.location);,因为我觉得直接分配它会传递一个稍后被覆盖的引用。想法?
    • 原始window.location指向的对象在这段代码中没有发生变化——window.location属性里面设置了不同的对象。由于原始窗口对象没有突变,因此无需克隆它。
    【解决方案7】:

    2020 年 @testing-library/react 的 window.location.assign 工作示例:

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

    【讨论】:

    • writable: true 是我的单元测试工作所必需的,否则后续测试将无法将其覆盖为其他内容。谢谢
    【解决方案8】:

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

    【讨论】:

      【解决方案9】:

      你可以试试帮手:

      const setURL = url => global.jsdom.reconfigure({url});
      
      describe('Test current location', () => {
        test('with GET parameter', () => {
          setURL('https://test.com?foo=bar');
          // ...your test here
        });
      });
      

      【讨论】:

        【解决方案10】:

        JSDOM 版本

        另一种方法,使用 JSDOM,将提供 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);
        

        【讨论】:

          【解决方案11】:

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

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

          【讨论】:

            【解决方案12】:

            根据上面和其他线程中的示例,这里有一个使用 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`
                    );
                });
            });
            

            【讨论】:

              【解决方案13】:

              这对 Jest + TypeScript + Next.js 有效(如果你使用 useRoute().push

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

              【讨论】:

                【解决方案14】:

                可能无关紧要。但是对于那些寻求 window.open('url', attribute) 解决方案的人来说,我在上面的一些 cmets 的帮助下应用了这个:

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

                【讨论】:

                  【解决方案15】:

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

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

                  【讨论】:

                    【解决方案16】:

                    你可以试试jest-location-mock

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

                    jest.config.js 文件或jest 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");
                    });
                    

                    【讨论】:

                      猜你喜欢
                      • 2020-12-04
                      • 1970-01-01
                      • 2021-03-17
                      • 2021-04-26
                      • 2018-12-25
                      • 2021-02-15
                      • 2021-03-04
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多