在Jest中嘲弄全局

And*_*rew 49 javascript dependencies unit-testing jestjs babel-jest

在Jest中有没有办法模拟全局对象,比如navigator,Image*?我已经非常放弃了这一点,并将其留给了一系列可模拟的实用方法.例如:

// Utils.js
export isOnline() {
    return navigator.onLine;
}
Run Code Online (Sandbox Code Playgroud)

测试这个微小的功能很简单,但很苛刻,而且根本不具有确定性.我可以获得75%的方式,但这是我可以做的:

// Utils.test.js
it('knows if it is online', () => {
    const { isOnline } = require('path/to/Utils');

    expect(() => isOnline()).not.toThrow();
    expect(typeof isOnline()).toBe('boolean');
});
Run Code Online (Sandbox Code Playgroud)

另一方面,如果我对这个间接是好的,我现在可以navigator通过这些实用程序访问:

// Foo.js
import { isOnline } from './Utils';

export default class Foo {
    doSomethingOnline() {
        if (!isOnline()) throw new Error('Not online');

        /* More implementation */            
    }
}
Run Code Online (Sandbox Code Playgroud)

......并确定性地测试这样......

// Foo.test.js
it('throws when offline', () => {
    const Utils = require('../services/Utils');
    Utils.isOnline = jest.fn(() => isOnline);

    const Foo = require('../path/to/Foo').default;
    let foo = new Foo();

    // User is offline -- should fail
    let isOnline = false;
    expect(() => foo.doSomethingOnline()).toThrow();

    // User is online -- should be okay
    isOnline = true;
    expect(() => foo.doSomethingOnline()).not.toThrow();
});
Run Code Online (Sandbox Code Playgroud)

在我使用的所有测试框架中,Jest感觉就像是最完整的解决方案,但是只要我编写笨拙的代码以使其可测试,我觉得我的测试工具让我失望.

这是唯一的解决方案还是我需要添加Rewire?

*不要假笑.Image非常适合ping远程网络资源.

And*_*rle 70

由于每个测试都运行自己的环境,因此只需覆盖它们就可以模拟全局变量.可以通过global名称空间访问所有全局变量.

global.navigator = {
  onLine: true
}
Run Code Online (Sandbox Code Playgroud)

覆盖仅对您当前的测试有效,不会影响其他测试.这也是处理Math.random或处理的好方法Date.now

注意,通过jsdom中的一些更改,你可能需要像这样模拟全局变量:

Object.defineProperty(globalObject, key, { value, writable: true });
Run Code Online (Sandbox Code Playgroud)

  • “覆盖仅对您当前的测试有影响,而不会影响其他测试。” -记录在任何地方吗? (7认同)
  • @JamesPlayer我可以肯定地确认,一个测试中的覆盖**将**影响其他测试。至少在一个测试套件中。 (3认同)

小智 13

这样做的正确方法是使用spyOn. 这里的其他答案,即使它们有效,也不考虑清理和污染全局范围。

// beforeAll
jest
  .spyOn(window, 'navigator', 'get')
  .mockImplementation(() => { ... })

// afterAll
jest.restoreAllMocks();
Run Code Online (Sandbox Code Playgroud)

  • 这给了我“属性导航器没有访问类型获取” - 这应该是哪个版本的 Jest 工作? (3认同)

小智 10

自从编写了接受的答案以来,Jest可能已更改,但是在测试之后,Jest似乎并未重置您的全局变量。请参阅所附的测试用例。

https://repl.it/repls/DecentPlushDeals

据我所知,唯一的解决方法是使用afterEach()afterAll()清理您对的分配global

  • 由于测试可以并行运行,因此即使在“afterEach()”中调用“jest.clearAllMocks()”也可能会失败。 (4认同)
  • 我遇到了同样的行为,每次测试运行后我的全局变量都没有重置。在 `afterEach()` 中调用 `jest.clearAllMocks();` 帮助了我 (2认同)

小智 8

如果有人需要使用静态属性模拟全局,那么我的示例应该会有所帮助:

  beforeAll(() => {
    global.EventSource = jest.fn(() => ({
      readyState: 0,
      close: jest.fn()
    }))

    global.EventSource.CONNECTING = 0
    global.EventSource.OPEN = 1
    global.EventSource.CLOSED = 2
  })
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您正在使用react-testing-library并且正在使用cleanup库提供的方法,则一旦文件中的所有测试运行完毕,它将删除该文件中所做的所有全局声明。这将不会延续到任何其他运行的测试。

例子:

import { cleanup } from 'react-testing-library'

afterEach(cleanup)

global.getSelection = () => {

}

describe('test', () => {
  expect(true).toBeTruthy()
})
Run Code Online (Sandbox Code Playgroud)