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.
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
unmockcalls 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
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
Most helpful comment
I had another look at this issue, and I've found a solution. Hoisting of
jest.mockstatements aboveimportstatements works, but with a caveat: apparently thejest.mockstatement won't be hoisted if it's not already above theimportstatement in the original code.This works:
This does not work:
So I wouldn't say that
jest.mockstatements are hoisted above typescriptimportstatements, but rather, typescriptimportstatements are hoisted, but not abovejest.mockstatements.@kulshekhar I think you can close this issue again, sorry about that.