簡體   English   中英

在 Jest 中模擬全局變量

[英]Mocking globals in Jest

Jest 中是否有任何方法可以模擬全局對象,例如navigatorImage *? 我幾乎放棄了這一點,把它留給了一系列可模擬的實用方法。 例如:

// Utils.js
export isOnline() {
    return navigator.onLine;
}

測試這個微小的函數很簡單,但很粗糙,根本不是確定性的。 我可以完成 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');
});

另一方面,如果我對這種間接方式沒有意見,我現在可以通過這些實用程序訪問navigator

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

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

        /* More implementation */            
    }
}

...並像這樣確定性地進行測試...

// 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();
});

在我使用過的所有測試框架中,Jest 感覺是最完整的解決方案,但是每當我編寫笨拙的代碼只是為了使其可測試時,我覺得我的測試工具讓我失望。

這是唯一的解決方案還是我需要添加重新布線?

*別笑。 Image非常適合 ping 遠程網絡資源。

由於每個測試套件都運行自己的環境,您可以通過覆蓋它們來模擬全局變量。 所有全局變量都可以通過global命名空間訪問:

global.navigator = {
  onLine: true
}

覆蓋僅對您當前的測試有影響,不會影響其他測試。 這也是處理Math.randomDate.now的好方法。

請注意,通過jsdom 中的一些更改,您可能必須像這樣模擬全局變量:

Object.defineProperty(globalObject, key, { value, writable: true });

Jest 可能在編寫接受的答案后發生了變化,但 Jest 似乎並沒有在測試后重置您的全局。 請參閱隨附的測試用例。

https://repl.it/repls/DecentPlushDeals

據我所知,解決這個問題的唯一方法是使用afterEach()afterAll()來清理您對global的分配。

let originalGlobal = global;
afterEach(() => {
  delete global.x;
})

describe('Scope 1', () => {
  it('should assign globals locally', () => {
    global.x = "tomato";
    expect(global.x).toBeTruthy()
  });  
});

describe('Scope 2', () => {
  it('should not remember globals in subsequent test cases', () => {
    expect(global.x).toBeFalsy();
  })
});

這樣做的正確方法是使用spyOn 這里的其他答案,即使它們有效,也不考慮清理和污染全局范圍。

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

// afterAll
jest.restoreAllMocks();

如果有人需要使用靜態屬性模擬全局,那么我的示例應該會有所幫助:

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

    global.EventSource.CONNECTING = 0
    global.EventSource.OPEN = 1
    global.EventSource.CLOSED = 2
  })

如果您正在使用react-testing-library並使用react-testing-library提供的cleanup方法,則一旦文件中的所有測試都運行,它將刪除該文件中所做的所有全局聲明。 這將不會延續到任何其他測試運行。

示例:

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

afterEach(cleanup)

global.getSelection = () => {

}

describe('test', () => {
  expect(true).toBeTruthy()
})

如果您需要在window.navigator上分配重新分配屬性的值,那么您需要:

  1. 聲明一個非常量變量
  2. 從全局/窗口對象返回它
  3. 更改原始變量的值(通過引用)

這將防止在嘗試重新分配window.navigator上的值時出錯,因為這些大多是只讀的。

let mockUserAgent = "";

beforeAll(() => {
  Object.defineProperty(global.navigator, "userAgent", {
    get() {
      return mockUserAgent;
    },
  });
});

it("returns the newly set attribute", () => {
  mockUserAgent = "secret-agent";
  expect(window.navigator.userAgent).toEqual("secret-agent");
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM