5

I'm trying to mock an async function that is exported as a default export but all I get is TypeError: Cannot read property 'then' of undefined

What I'm trying to mock is config.js:

const configureEnvironment = async (nativeConfig) => {
    return { await whatever() }
}

The file I'm testing is Scene.js:

import configureEnvironment from './config';

class Scene extends React.Component {
    constructor(props) {
        nativeConfig = {};
        configureEnfironment(nativeConfig).then((config) => {
            // Do stuff
        }
    }
}

And my test file is Scene.test.js:

let getScene = null;
const configureEnvironmentMock = jest.fn();

describe('Scene', () => {
    jest.mock('./config', () => configureEnvironmentMock);

    const Scene = require('./Scene').default;

    getScene = (previousState) => {
        return shallow(
            <Scene prevState={previousState}>
                <Fragment />
            </Scene>,
        );
    };

    it('calls configureEnvironment with the nativeConfig', async () => {
        expect.assertions(1);
        const nativeConfig = {};

        getScene(nativeConfig);

        expect(configureEnvironmentMock).toHaveBeenCalledWith(nativeConfig);
    });
});

However, the result of running the test is:

TypeError: Cannot read property 'then' of undefined

I understand the issue is on the way I mock configureEnvironment but I cannot get it working.

I also tried to mock the function like:

jest.mock('./config', () => {
    return {
        default: configureEnvironmentMock,
    };
});

But it results on:

TypeError: (0 , _config2.default) is not a function

2 Answers 2

2

A clean and simple way to mock the default export of a module is to use jest.spyOn in combination with functions like mockImplementation.


Here is a working example based on the code snippets above:

config.js

const whatever = async () => 'result';

const configureEnvironment = async (nativeConfig) => await whatever();

export default configureEnvironment;

Scene.js

import * as React from 'react';
import configureEnvironment from './config';

export class Scene extends React.Component {
  constructor(props) {
    super(props);
    configureEnvironment(props.prevState).then((config) => {
      // Do stuff
    });
  }
  render() {
    return null;
  }
}

Scene.test.js

import React, { Fragment } from 'react';
import { shallow } from 'enzyme';

import { Scene } from './Scene';
import * as config from './config';

describe('Scene', () => {

  const mock = jest.spyOn(config, 'default'); // spy on the default export of config
  mock.mockImplementation(() => Promise.resolve('config')); // replace the implementation

  const getScene = (previousState) => {
    return shallow(
      <Scene prevState={previousState}>
        <Fragment />
      </Scene>,
    );
  };

  it('calls configureEnvironment with the nativeConfig', async () => {
    expect.assertions(1);
    const nativeConfig = {};

    getScene(nativeConfig);

    expect(mock).lastCalledWith(nativeConfig);  // SUCCESS
  });
});
Sign up to request clarification or add additional context in comments.

4 Comments

Works perfect. The key thing for me was the ability to import as import * as config from './config'; allowing then to spy on the default export.
I get 'Cannot spy the default property because it is not a function; object given instead'. I guess this only works if the export is a function
@Marc yes, this approach is for when the default export is a function
how to use this for named exports?
0

You can mock anything with jest, like this jest.mock('@material-ui/core/withWidth', () => ({ __esModule: true, isWidthUp: jest.fn((a, b) => true), default: jest.fn(fn => fn => fn) }))

1 Comment

And how do you restore it after this test?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.