Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking functions of jest not working (need to babel-jest) #90

Closed
Bnaya opened this issue Dec 28, 2016 · 25 comments
Closed

Mocking functions of jest not working (need to babel-jest) #90

Bnaya opened this issue Dec 28, 2016 · 25 comments

Comments

@Bnaya
Copy link
Contributor

Bnaya commented Dec 28, 2016

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.

@kulshekhar
Copy link
Owner

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 :)

@tkrotoff
Copy link
Contributor

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

@bhouser
Copy link

bhouser commented Feb 2, 2017

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

@kulshekhar
Copy link
Owner

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

@bhouser
Copy link

bhouser commented Feb 2, 2017

@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.

@kulshekhar kulshekhar reopened this Feb 3, 2017
@bhouser
Copy link

bhouser commented Feb 6, 2017

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.

@kulshekhar
Copy link
Owner

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

@tkrotoff
Copy link
Contributor

tkrotoff commented Feb 6, 2017

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

@kulshekhar
Copy link
Owner

@tkrotoff fair enough.

Would it be possible for you to submit a PR?

@kulshekhar kulshekhar reopened this Feb 6, 2017
@asfernandes
Copy link

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';

@bhouser
Copy link

bhouser commented Feb 6, 2017

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

@asfernandes
Copy link

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.

@kamek-pf
Copy link

kamek-pf commented Mar 29, 2017

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 ?

@Bnaya Bnaya closed this as completed May 13, 2017
@Bnaya
Copy link
Contributor Author

Bnaya commented May 13, 2017

Looks like its working with latest ts-jest

Thanks for the great work!

@GeeWee
Copy link
Collaborator

GeeWee commented May 20, 2017

@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.

@xealot
Copy link

xealot commented Jun 8, 2017

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.

@xealot
Copy link

xealot commented Jun 8, 2017

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.

@kulshekhar
Copy link
Owner

@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?

@xealot
Copy link

xealot commented Jun 9, 2017

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

@mrdulin
Copy link

mrdulin commented Jul 6, 2017

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?

@kulshekhar
Copy link
Owner

@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

@GeeWee
Copy link
Collaborator

GeeWee commented Jul 6, 2017

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.

@mrdulin
Copy link

mrdulin commented Jul 7, 2017

@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

@GeeWee
Copy link
Collaborator

GeeWee commented Jul 7, 2017

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?

@yleflour
Copy link

yleflour commented Mar 6, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants