Ts-jest: jest.mock() makes module undefined when importing it no matter what we return in factory function

Created on 2 Mar 2017  Â·  24Comments  Â·  Source: kulshekhar/ts-jest

./src/StatelessComponent.tsx

import * as React from 'react';
import ChildComponent from './ChildComponent';

const StatelessComponent = () => (
  <div>  
    <ChildComponent />
  </div>
);

export default StatelessComponent;

./src/ChildComponent.tsx

import * as React from 'react';

const ChildComponent = () => (<b>Hello</b>);

export default ChildComponent;

./__tests__/StatelessComponent.test.tsx

jest.mock('../src/ChildComponent', () => 'ChildComponent');

// rest of the test here

When running the test in TS, ChildComponent is undefined.
When running the test in ES6, ChildComponent is defined.

Can be related to this issue in Jest repo: https://github.com/facebook/jest/issues/2984

Most helpful comment

Ok I have a solution (actually my genius coworker @junseld found it).

It appears to have something to do with the way import/export behaves in Typescript compared to ES6.

Instead of this:

jest.mock('../src/ChildComponent', () => 'ChildComponent');

Do this:

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

I guess default export in ES6 allows the exported value to be imported directly, but Typescript creates a named export called "default". So your mock needs to be an object with a default key: {default: theDefaultExport}

All 24 comments

Ok I have a solution (actually my genius coworker @junseld found it).

It appears to have something to do with the way import/export behaves in Typescript compared to ES6.

Instead of this:

jest.mock('../src/ChildComponent', () => 'ChildComponent');

Do this:

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

I guess default export in ES6 allows the exported value to be imported directly, but Typescript creates a named export called "default". So your mock needs to be an object with a default key: {default: theDefaultExport}

Maybe we can update the ts-jest documentation to warn users about the differences in mocking compared to ES6?

+1000

+1000

Oh, I created a minimal repo a while ago, when I thought the problem was Jest:
https://github.com/bhouser/jestReactMockTsx

Sorry about not posting it sooner.

@bhouser thanks.

The tests in the typescript directory of the linked repo pass if "allowSyntheticDefaultImports": true is added to tsconfig.json

@MichalCafe does this solve the issue for you as well? ^

20.0.6 was just published which now hoists mock calls.

@MichalCafe @bhouser can you check whether this fixes your issues?

To mock an ES6 dependency module default export using jest:

Instead of:

jest.mock('../src/ChildComponent', () => 'ChildComponent');

What worked for me was:

import ChildComponent from '../src/ChildComponent';
jest.mock('../src/ChildComponent');
ChildComponent.mockImplementation(() => 'ChildComponent');

The other options didn't work for me.

Hey, I encounter similar issue. @adrifmonte when you do jest.mock('../src/ChildComponent'); and ChildComponent.mockImplementation(() => 'ChildComponent'); , does it show ts error saying that 'mockImplementation does not exist on type ChildComponent' . Please let me know

@dmngu9 be sure to NOT have skipBabel: true in globals > ts-jest of jest configuration, else jest.mock calls won't be hoisted.
Also you can try adding esModuleInterop: true to your tsconfig.json if you have other issues like the original one of this thread.

@huafu still not work for me :cry: . Also whenjest.mock(module, () => <div>ju</div>), it wont allow because it reference to React which is out of scope of jest.mock

@dmngu9 can you create a minimal repo with the issue?

@huafu i used this repo https://github.com/bhouser/jestReactMockTsx. The typescript folder

@dmngu9 almost all dependencies are way too old out there ;-)

I made it work with this

$ jest
 PASS  src/__tests__/ParentComponent_test.tsx
  ✓ renders correctly (14ms)

 › 1 snapshot written.
Snapshot Summary
 › 1 snapshot written from 1 test suite.

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 written, 1 total
Time:        1.675s
Ran all test suites.
✨  Done in 3.48s.

@huafu thanks but my question is to do mocking in these 2 ways:
1)
` import ChildComponent from '../src/ChildComponent';

jest.mock('../src/ChildComponent');

ChildComponent.mockImplementation(() => 'ChildComponent')`

2)
`jest.mock('../src/ChildComponent', () => {
const React = require('react')

component = () => <div id="child" />;

return component;

});`

actually option 1 works for me when run jest. However on vscode, it keeps complaining mockImplementation not exist on ChildComponent even though running jest works

I've just tried (within the repo with the changes I sent above):

diff --git a/typescript/src/__mocks__/ChildComponent.tsx b/typescript/src/__mocks__/ChildComponent.tsx
new file mode 100644
index 0000000..1263b16
--- /dev/null
+++ b/typescript/src/__mocks__/ChildComponent.tsx
@@ -0,0 +1 @@
+export default jest.fn(() => 'ChildComponent')
\ No newline at end of file
diff --git a/typescript/src/__tests__/ParentComponent_test.tsx b/typescript/src/__tests__/ParentComponent_test.tsx
index 4a79a8f..12a1e2a 100644
--- a/typescript/src/__tests__/ParentComponent_test.tsx
+++ b/typescript/src/__tests__/ParentComponent_test.tsx
@@ -1,9 +1,9 @@
-jest.mock('../ChildComponent', () => 'ChildComponent');
-
 import * as React from 'react';
 import * as renderer from 'react-test-renderer';
 import ParentComponent from "../ParentComponent";

+jest.mock('../ChildComponent');
+
 test('renders correctly', () => {
     const tree = renderer.create(
         <ParentComponent />
diff --git a/typescript/src/__tests__/__snapshots__/ParentComponent_test.tsx.snap b/typescript/src/__tests__/__snapshots__/ParentComponent_test.tsx.snap
new file mode 100644
index 0000000..2289842
--- /dev/null
+++ b/typescript/src/__tests__/__snapshots__/ParentComponent_test.tsx.snap
@@ -0,0 +1,10 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<div
+  className="parentComponent"
+>
+  ChildComponent
+  ;
+</div>
+`;

as well as:

diff --git a/typescript/src/__tests__/ParentComponent_test.tsx b/typescript/src/__tests__/ParentComponent_test.tsx
index 4a79a8f..01c2b1b 100644
--- a/typescript/src/__tests__/ParentComponent_test.tsx
+++ b/typescript/src/__tests__/ParentComponent_test.tsx
@@ -1,8 +1,12 @@
-jest.mock('../ChildComponent', () => 'ChildComponent');
-
 import * as React from 'react';
 import * as renderer from 'react-test-renderer';
 import ParentComponent from "../ParentComponent";
+import ChildComponent from '../ChildComponent';
+
+jest.mock('../ChildComponent');
+
+(ChildComponent as jest.Mock).mockImplementation(() => 'ChildComponent')
+

 test('renders correctly', () => {
     const tree = renderer.create(

they both work

vscode complains because the type isn't and can't be changed, see #576

@huafu thanks for the help. All good now. It is interesting when using jest.mock(module, factory), it differentiate between default and named import. But when using mockImplementation, it does not

I ask to reopen this issue due to lacking documentation / warnings of this issue.

The fact that create-react-app-typescript by default creates projects where mocking as documented doesn't work is a big problem that had me stumbled for hours.

That problem wouldn't be as dire if ts-jest detected that a combination of typescript configs and jest.mock with second parameter function that returns a undefined and gave a proper warning/error message. This case might be where import '' in typescript never returns a undefined either way (not sure about dynamic import tho).

this worked for me:
jest.mock('~/some/path')
import ModuleToMock from '~/some/path'
class MockedClass {
...
}
ModuleToMock = MockedClass

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bySabi picture bySabi  Â·  4Comments

qm3ster picture qm3ster  Â·  3Comments

Slessi picture Slessi  Â·  3Comments

bruk1977 picture bruk1977  Â·  3Comments

artola picture artola  Â·  3Comments