Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use imported variables in jest mock function

Tags:

reactjs

jestjs

I am mocking a function from a file which uses moment, Here is the file contents:

./utils/dateUtils:

import moment from 'moment-timezone'

export function getToday() {
  return moment().tz(commonTimeZone)
}


./containers/someContainer.js:

import { getToday } from 'utils/dateUtils'

// Uses getToday in the component


./containers/someContainer.spec.js:

import moment from 'moment-timezone'

jest.mock('utils/dateUtils', () => {
  return {
    getToday : moment(new Date('2018-01-01'))
  }
})

test throws this error:

● Test suite failed to run

    /Users/bharat/Documents/redmart-repo/partner-portalv2/app/containers/Orders/PickupsContainer.test.js: babel-plugin-jest-hoist: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: moment
    Whitelisted objects: Array, ArrayBuffer, Boolean, DataView, Date, Error, EvalError, Float32Array, Float64Array, Function, Generator, GeneratorFunction, Infinity, Int16Array, Int32Array, Int8Array, InternalError, Intl, JSON, Map, Math, NaN, Number, Object, Promise, Proxy, RangeError, ReferenceError, Reflect, RegExp, Set, String, Symbol, SyntaxError, TypeError, URIError, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, WeakMap, WeakSet, arguments, expect, jest, require, undefined, DTRACE_NET_SERVER_CONNECTION, DTRACE_NET_STREAM_END, DTRACE_HTTP_SERVER_REQUEST, DTRACE_HTTP_SERVER_RESPONSE, DTRACE_HTTP_CLIENT_REQUEST, DTRACE_HTTP_CLIENT_RESPONSE, global, process, Buffer, clearImmediate, clearInterval, clearTimeout, setImmediate, setInterval, setTimeout, console.
    Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with `mock` are permitted.

      at invariant (node_modules/babel-plugin-jest-hoist/build/index.js:12:11)
      at newFn (node_modules/babel-traverse/lib/visitors.js:276:21)
      at NodePath._call (node_modules/babel-traverse/lib/path/context.js:76:18)
      at NodePath.call (node_modules/babel-traverse/lib/path/context.js:48:17)
      at NodePath.visit (node_modules/babel-traverse/lib/path/context.js:105:12)
      at TraversalContext.visitQueue (node_modules/babel-traverse/lib/context.js:150:16)
      at TraversalContext.visitMultiple (node_modules/babel-traverse/lib/context.js:103:17)

Not sure what am missing here, does someone know how to fix this error.

P.S. I tried jest.doMock but it doesn't mock anything.

like image 729
Bharat Soni Avatar asked Jul 04 '18 07:07

Bharat Soni


2 Answers

Your problem is that jest.mock calls are hoisted, which means they are put at the very top of the file. It's kind of tricky to find it in the docs, but they say it under jest.doMock.

That makes your executed code to look something like this:

jest.mock('utils/dateUtils', () => {
  return {
    getToday : moment(new Date('2018-01-01'))
  }
})

import moment from 'moment-timezone'

And as you can see moment is used before imported, and thus the error message about referencing out-of-scope variables.

There are multiple ways to avoid this.

However, using jest.doMock should have avoided the hoisting. I wonder if the path is missing something like '../utils... –note the initial ../

There's also the fact that you are defining getToday's mock as a value instead of a function, which might have made your tests fail when the module was actually mocked.

Anyways, there are two main ways to mock the module

ES6 Class Mock

The idea is to first mock the import and then define the mocked implementation.

import moment from 'moment-timezone'
import * as dateUtils from 'utils/dateUtils'

jest.mock('utils/dateUtils')

dateUtils.getToday = () => moment(new Date('2018-01-01'))

Manual Mock

Here you create a mock file with the mocked implementation of the module that will be imported wherever you mock the module. That mock file has to live in a __mocks__ folder which needs to be at the same level of the file you want to mock. For example:

├── utils
│   ├── __mocks__
│   │   └── dateUtils.js
│   └── dateUtils.js
│
├── other folders

the code would look like this:

// utils/__mocks__/dateUtils.js
import moment from 'moment-timezone'

export const getToday = () => moment(new Date('2018-01-01'))

And the tests:

// containers/someContainer.spec.js
import * from './path/to/utils/dateUtils'

jest.mock('./path/to/utils/dateUtils')

// your tests here

like image 152
Daniel Reina Avatar answered Nov 12 '22 11:11

Daniel Reina


You need to require the packages locally in a mock, this is to ensure no external variables are used -

jest.mock('utils/dateUtils', () => {
  const moment = require("moment-timezone");
  return {
    getToday : moment(new Date('2018-01-01'))
  }
})

Here's the link to the relevant issue - https://github.com/facebook/jest/issues/2567.

like image 34
hazardous Avatar answered Nov 12 '22 11:11

hazardous