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

时间:2019-01-03 11:02:54

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

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

我尝试了一些解决方案,但没有解决。我需要一些提示来帮助我摆脱困境。请帮助。

14 个答案:

答案 0 :(得分:26)

2020更新


基本

URL objectLocation 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

答案 1 :(得分:9)

您可以尝试:

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

答案 2 :(得分:6)

2019年的解决方案from GitHub

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

答案 3 :(得分:6)

最好是创建一个新的URL实例,以便像location.href一样解析您的字符串,并像{{1}一样更新location的所有属性。 },.hash.search

.protocol

https://repl.it/repls/VoluminousHauntingFunctions

答案 4 :(得分:4)

提供的许多示例并未模拟原始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);

        // ...
    });
});

答案 5 :(得分:2)

我已通过添加writable: true并将其移至beforeEach

解决了此问题

这是我的示例代码:

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

答案 6 :(得分:2)

在2020年使用@testing-library/react处理window.location.assign的示例:

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

答案 7 :(得分:1)

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

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

根据上述示例和其他示例,这是一个使用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`
        );
    });
});

答案 10 :(得分:0)

您可以尝试一个助手:

const setURL = url => global.jsdom.reconfigure({url});

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

答案 11 :(得分:0)

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

const oldWindowLocation = window.location;

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

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

答案 12 :(得分:0)

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

答案 13 :(得分:0)

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

<script type='application/ld+json'>
    {
      "@context": "http://schema.org",
      "@type": "Invoice",
      "accountId": "123-456-789",
      "minimumPaymentDue": {
        "@type": "PriceSpecification",
        "price": "$70.00"
      },
      "paymentDue": "2015-11-22T08:00:00+00:00",
      "paymentStatus": "PaymentAutomaticallyApplied",
      "provider": {
        "@type": "Organization",
        "name": "Mountain View Utilities"
      },
      "totalPaymentDue": {
        "@type": "PriceSpecification",
        "price": "$70.00"
      }
    }
    </script>