Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking globals in Jest

Is there any way in Jest to mock global objects, such as navigator, or Image*? I've pretty much given up on this, and left it up to a series of mockable utility methods. For example:

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

Testing this tiny function is simple, but crufty and not deterministic at all. I can get 75% of the way there, but this is about as far as I can go:

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

On the other hand, if I am okay with this indirection, I can now access navigator via these utilities:

// Foo.js import { isOnline } from './Utils';  export default class Foo {     doSomethingOnline() {         if (!isOnline()) throw new Error('Not online');          /* More implementation */                 } } 

...and deterministically test like this...

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

Out of all the testing frameworks I've used, Jest feels like the most complete solution, but any time I write awkward code just to make it testable, I feel like my testing tools are letting me down.

Is this the only solution or do I need to add Rewire?

*Don't smirk. Image is fantastic for pinging a remote network resource.

like image 1000
Andrew Avatar asked Nov 06 '16 12:11

Andrew


People also ask

How do you mock global in Jest?

If you need to mock a global variable for all of your tests, you can use the setupFiles in your Jest config and point it to a file that mocks the necessary variables. This way, you will have the global variable mocked globally for all test suites.

How are global variables defined in Jest?

"jest": { "globals": { "__DEV__": true, "__RCTProfileIsProfiling": false }, ... }, This will make the variables available globally when the tests are run. You can also access the global variables like global. __DEV__ , if needed.

Can you mock an object in Jest?

The jest object is automatically in scope within every test file. The methods in the jest object help create mocks and let you control Jest's overall behavior. It can also be imported explicitly by via import {jest} from '@jest/globals' .

How to mock global object methods in jest?

A common problem is poorly-written mocks, especially regarding global objects and their methods. Let's take a look at how to mock global object methods in Jest. When mocking global object methods in Jest, the optimal way to do so is using the jest.spyOn () method.

How do you Mock a window location in jest?

Mocking already existing window variables As we can see tested function uses globally available window.location variables. Those variables are provided by jsdom by default which let's us to mock them using built-in jest methods jest.spyOn (), .mockImplementation () and restore with .mockRestore ().

How to mock Globals in a test suite?

As every test suite run its own environment, you can mock globals by just overwriting them. All global variables can be accessed by the global namespace:

How do I mock a non-existent Globals in jsdom?

Mocking non-existent globals In case of variables which are globally available but are not provided by jsdom by default e.g. various API libraries like Google Maps API google or window.google variable the only option is to mock them by direct assignment at the beginning of the test.


2 Answers

As every test suite run its own environment, you can mock globals by just overwriting them. All global variables can be accessed by the global namespace:

global.navigator = {   onLine: true } 

The overwrite has only effects in your current test and will not effect others. This also a good way to handle Math.random or Date.now.

Note, that through some changes in jsdom it could be possible that you have to mock globals like this:

Object.defineProperty(globalObject, key, { value, writable: true }); 
like image 178
Andreas Köberle Avatar answered Oct 13 '22 06:10

Andreas Köberle


Jest may have changed since the accepted answer was written, but Jest does not appear to reset your global after testing. Please see the testcases attached.

https://repl.it/repls/DecentPlushDeals

As far as I know, the only way around this is with an afterEach() or afterAll() to clean up your assignments to 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();   }) }); 
like image 27
smeltedcode Avatar answered Oct 13 '22 06:10

smeltedcode