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

jest.mock() makes module undefined when importing it no matter what we return in factory function #120

Closed
pietmichal opened this issue Mar 2, 2017 · 27 comments · Fixed by bhouser/jestReactMockTsx#1

Comments

@pietmichal
Copy link

./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: jestjs/jest#2984

@bhouser
Copy link

bhouser commented Mar 2, 2017

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}

@bhouser
Copy link

bhouser commented Mar 2, 2017

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

@wilomgfx
Copy link

+1000

@mithrand
Copy link

+1000

@bhouser
Copy link

bhouser commented Apr 27, 2017

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.

@kulshekhar
Copy link
Owner

kulshekhar commented Apr 27, 2017

@bhouser thanks.

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

@kulshekhar
Copy link
Owner

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

@kulshekhar
Copy link
Owner

20.0.6 was just published which now hoists mock calls.

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

@adrifmonte
Copy link

adrifmonte commented Aug 11, 2017

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.

@dmngu9
Copy link

dmngu9 commented Jul 25, 2018

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

@huafu
Copy link
Collaborator

huafu commented Jul 25, 2018

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

@dmngu9
Copy link

dmngu9 commented Jul 25, 2018

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

@huafu
Copy link
Collaborator

huafu commented Jul 25, 2018

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

@dmngu9
Copy link

dmngu9 commented Jul 25, 2018

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

@huafu
Copy link
Collaborator

huafu commented Jul 25, 2018

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

@huafu
Copy link
Collaborator

huafu commented Jul 25, 2018

I made it work with this

@huafu
Copy link
Collaborator

huafu commented Jul 25, 2018

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

@dmngu9
Copy link

dmngu9 commented Jul 25, 2018

@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')`

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

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

return component;

});`

@dmngu9
Copy link

dmngu9 commented Jul 25, 2018

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

@huafu
Copy link
Collaborator

huafu commented Jul 25, 2018

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

@huafu
Copy link
Collaborator

huafu commented Jul 25, 2018

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

@dmngu9
Copy link

dmngu9 commented Jul 25, 2018

@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

@Pokute
Copy link

Pokute commented Jul 31, 2018

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

@Shmigelskiy
Copy link

Shmigelskiy commented Mar 18, 2019

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

@EdwardHinkle
Copy link

This is still an issue in 2022.

@BismitaRath
Copy link

Facing same issue with the jest.mock("@opentelemetry/core")

@rwilliams3088
Copy link

+1 in late 2023; running into this issue while trying to mock an RTK Query Api

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

Successfully merging a pull request may close this issue.