Ts-jest: Mocking functions of jest not working (need to babel-jest)

Created on 28 Dec 2016  ·  25Comments  ·  Source: kulshekhar/ts-jest

For the modules mocking of jest to work with babel, they made special babel preset to hoist the calls above the require() calls
https://www.npmjs.com/package/babel-plugin-jest-hoist

In order to make ts-jest work properly as babel-jest we need this transformation also.

Documentation Help Wanted

Most helpful comment

I had another look at this issue, and I've found a solution. Hoisting of jest.mock statements above import statements works, but with a caveat: apparently the jest.mock statement won't be hoisted if it's not already above the import statement in the original code.

This works:

jest.mock('../pleaseMockMe')
import { iHaveBeenMocked } from '../pleaseMockMe'

This does not work:

import { iHaveBeenMocked } from '../pleaseMockMe'
jest.mock('../pleaseMockMe')

So I wouldn't say that jest.mock statements are hoisted above typescript import statements, but rather, typescript import statements are hoisted, but not above jest.mock statements.

@kulshekhar I think you can close this issue again, sorry about that.

All 25 comments

I can't commit to this as there's a lot on my plate right now. I'd be open to a PR with tests though :)

Related comment here:

[...] When using babel-jest, we actually hoist unmock calls above imports. If you are using TypeScript, we don't have this custom transform available unfortunately.

Here are a few ways to work around this issue:

  • [...]
  • [...]
  • Use babel-jest in your typescript preprocessor after processing your typescript code into JS. (basically: return babelJest.process(tsOutput, filename); ).
  • [...]

Found this https://github.com/lozinsky/typescript-babel-jest that does exactly that: https://github.com/lozinsky/typescript-babel-jest/blob/234de0a5832f85061115655959339e7ee055663c/source/preprocessor.js#L23

Why is this closed? Is the problem solved, because I'm having issues mocking Typescript imports.

@bhouser I'm not sure why this was closed. If you're facing this exact issue, I can reopen it

@kulshekhar I'm having exactly this issue yes. Typescript imports are not mocked, jest.mock('module') calls have no effect. I can't guarantee that I don't have any other issues in my code, but if the mock commands are not hoisted above the import statements, I don't see how this jest feature can work.

I had another look at this issue, and I've found a solution. Hoisting of jest.mock statements above import statements works, but with a caveat: apparently the jest.mock statement won't be hoisted if it's not already above the import statement in the original code.

This works:

jest.mock('../pleaseMockMe')
import { iHaveBeenMocked } from '../pleaseMockMe'

This does not work:

import { iHaveBeenMocked } from '../pleaseMockMe'
jest.mock('../pleaseMockMe')

So I wouldn't say that jest.mock statements are hoisted above typescript import statements, but rather, typescript import statements are hoisted, but not above jest.mock statements.

@kulshekhar I think you can close this issue again, sorry about that.

@bhouser no problem! Hopefully others facing this issue will be able to use the workaround you've posted :smile:

@kulshekhar this shouldn't be closed until the workaround is documented in README.md: this will save others a lot of troubles

@tkrotoff fair enough.

Would it be possible for you to submit a PR?

Note that due to dependency chain, the mock calls must be in the file start and not just before each import.

I had this case (note it does not mock exactly the imported file, but one of its dependencies) and just putting mock before the import didn't solved.

jest.mock('../../services/authApi');
import { authService } from '../../services';

@asfernandes I'm not sure I understand exactly what you mean. Are you saying, all mock calls must be before the first import statement?

For my case it worked putting before the first import, but I have only 3 test cases. Is it neccessary to be in the start? I don't know, but for sure it didn't worked after some imports unrelated to what I was mocking.

This doesn't work if you need to mock one specific function of a module though.
Small repro :

// myModule.ts

export const mainFunction = () => {
    return helperFunction();
};

// I want to call mainFunction and have it use a mock of the following :
const helperFunction = () => {
    console.log('Actual implementation called !');

    return 42;
};
// myMockedModule.spec.ts

jest.mock('../src/myModule', () => ({
    ...require.requireActual('../src/myModule'),
    helperFunction: jest.fn(() => console.log('Mocked implementation called !'))
}));

// These all behave the same
const MyModule = require('../src/myModule');
// import * as MyModule from '../src/myModule';
// const MyModule = require.requireMock('../src/myModule');

test('With mocks', () => {
    expect(MyModule.mainFunction()).toBe(undefined); // always returns 42, mock is never used
});

Still, maybe I'm missing something. Any bright ideas ?

Looks like its working with latest ts-jest

Thanks for the great work!

@Bnaya are you saying that for you, currently the order of jest.mock('foo') and import statements do not matter? I still have to hoist them up manually.

I am new to Jest, but I can't get this feature to work at all. Whether the mock() call is above or below the import.

I stand corrected, during the course of my debugging this feature I wrote:

const t = jest.mock("...")

Which, (perhaps surprisingly, perhaps not) does not work.

Removing the assignment it begins working again. Apologies for the email spam.

@xealot the fix for mocks has gone in but hadn't been published. I've just published it. Can you try with 20.0.6?

It does seem to work still, if that's any consolation. But I fixed my issue by dropping the assignment and adding allowSyntheticDefaultImports.

This is a frustrating issue,
I use webpack2 + jest + test-jest + typescript + react.

And, I use ts-loader, my tsconfig module is commonjs and target is es5

./myModule.ts:

function b() {
  return `Emily ${a()}`;
}

function a(): string {
  return 'is Away';
}

export {
  b
};

./myModule.test.ts:

///<reference path="../../../node_modules/@types/jest/index.d.ts"/>

import {b} from './myModule';

describe('mockFunction test suites', () => {

  it('t-1', () => {

    const aImplementation = (): string => {
      return 'is coming';
    };

    //mock failed, always return "is Away". I expect `a()` return "is coming".
    const a: jest.Mock<string> = jest.fn(aImplementation); 

    const actualValue = b();
    const expectValue = 'Emily is coming';

    expect(actualValue).toBe(expectValue);

  });

});

Here is the test results:

FAIL  src/utils/__tests__/mockFunction.test.ts
  ● mockFunction test suites › t-1

    expect(received).toBe(expected)

    Expected value to be (using ===):
      "Emily is coming"
    Received:
      "Emily is Away"

      at Object.<anonymous> (src/utils/__tests__/mockFunction.test.ts:21:25)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
      at process._tickCallback (internal/process/next_tick.js:109:7)

Can anybody give me a way to solve this issue?

@mrdulin it'll be hard to help in any meaningful way without a minimal repo that can help us debug this issue. Without a minimal repo that replicates your setup, we can't be sure where the problem is. Once you have created one, open a new issue with the details

Do you expect your jest.mock() of 'a' to be called by b? I'm reasonably sure that's not how jest, or javascript works in general.

@GeeWee But instead of this export const xxx way. If I put these method to a class or an object literal or commonjs exports.a = function() {} exports.b = function() { exports.a() },
I can mock the method a successfully.

Please see my examples : https://github.com/mrdulin/typescript-playground/tree/master/jest-examples , You can just take a look about mock-function-xxx directories

Yes that makes sense to me.
//mock failed, always return "is Away". I expect a() return "is coming".
const a: jest.Mock = jest.fn(aImplementation); <- creates a new a,

Where as if it's on an object you can actually change the reference.

If you run this exact same setup without ts-jest, and typescript, does it then work as intended?

In case anyone goes through this issue, jest introduced a jest.doMock function.
This method is not hoisted and will work through tsc => babel transpiling

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GeeWee picture GeeWee  ·  4Comments

stephenotalora picture stephenotalora  ·  3Comments

ahnpnl picture ahnpnl  ·  3Comments

TKJohn picture TKJohn  ·  4Comments

qm3ster picture qm3ster  ·  3Comments