react-native: 0.61.0 - Jest "Cannot find module ..."
After upgrading from 0.60.5
to 0.61.0
, when I run Jest tests I will get errors anywhere I was mocking a component or native module from React Native using the syntax where I mock the module/component by name rather than providing a path to it or mocking all of React Native, i.e:
jest.mock('TextInput', () => {
...
})
This has always worked in previous versions of React Native and is still mentioned as a valid way to mock things in the jest docs.
It looks like I can fix the issue by mocking all of React Native and overriding the specific modules I care about, however I’m curious if this change was intentional or not. If it is, then I can move this issue to the Jest repo and let them know they need to update their docs.
React Native version: System: OS: macOS 10.14.6 CPU: (8) x64 Intel® Core™ i7-7920HQ CPU @ 3.10GHz Memory: 1.01 GB / 16.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 8.11.3 - ~/.nvm/versions/node/v8.11.3/bin/node Yarn: 1.16.0 - /usr/local/bin/yarn npm: 5.6.0 - ~/.nvm/versions/node/v8.11.3/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman SDKs: iOS SDK: Platforms: iOS 13.0, DriverKit 19.0, macOS 10.15, tvOS 13.0, watchOS 6.0 Android SDK: Build Tools: 25.0.2 IDEs: Android Studio: 3.5 AI-191.8026.42.35.5791312 Xcode: 11.0/11A420a - /usr/bin/xcodebuild npmPackages: react: 16.9.0 => 16.9.0 react-native: 0.61.1 => 0.61.1 npmGlobalPackages: react-native-cli: 2.0.1
Steps To Reproduce
- Using a 0.61.0 build of RN, write a Jest test(doesn’t have to assert anything in particular).
- In that test, mock a React Native library component by adding
jest.mock('View')
- Run the test
Describe what you expected to happen:
Jest should fail with an error telling you Cannot find module 'View' ...
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 16
- Comments: 37 (5 by maintainers)
Commits related to this issue
- fix NativeModule mocks, as per https://github.com/facebook/react-native/issues/26579#issuecomment-538610849 — committed to developmentseed/observe by batpad 5 years ago
- jest: Mock `react-native` ourselves. The immediate need is to set up a way to mock the new `ZLPConstants` in `NativeModules`, which we'll do in an upcoming commit. Doing this, as recommended by Reac... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock `react-native` ourselves. Doing this, as recommended by React Native [1], means we're better prepared for the React Native v0.61 upgrade (#3781), in which Haste is removed [2]. A consequen... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock "Linking" the right way. In the upcoming RN v0.60 -> v0.61 upgrade, Haste won't be used, so the string "Linking" won't be resolved. Move our "Linking" mock into our mock of 'react-native'... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock `react-native` ourselves. The immediate need is to set up a way to mock the new `ZLPConstants` in `NativeModules`, which we'll do in an upcoming commit. Doing this, as recommended by Reac... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock `react-native` ourselves. Doing this, as recommended by React Native [1], means we're better prepared for the React Native v0.61 upgrade (#3781), in which Haste is removed [2]. A consequen... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock "Linking" the right way. In the upcoming RN v0.60 -> v0.61 upgrade, Haste won't be used, so the string "Linking" won't be resolved. Move our "Linking" mock into our mock of 'react-native'... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock "Linking" from `react-native` properly. With something called Haste, we were allowed to just pass "Linking" to `jest.mock`, and it would automagically be known that we want something calle... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock "Linking" from `react-native` properly. With something called Haste, we were allowed to just pass "Linking" to `jest.mock`, and it would automagically be known that we want something calle... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Mock "Linking" from `react-native` properly. With something called Haste, we were allowed to just pass "Linking" to `jest.mock`, and it would automagically be known that we want something calle... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
I was able to successfully mock the individual modules by mocking the direct path in react-native so items like
can become
Prior to rn 61 haste was used to map these files. In haste the mapping is done by filename so it’s easy enough to find the corresponding file in the react-native repo by using find files search provided by github.
@fossage This is a working example that mocks a NativeModule and parts of a JS module (LayoutAnimation). It lets you import a pre-mocked instance of
react-native
, override whatever you’d like, and then access those mocks from your tests:jest.config.js
setup.js
__tests__/example-test.js
Output
The main limitation that comes to mind is that you need to use the same
doMock
call for all tests and Jest’s flexibility often offers ways to work around that.In case this is helpful for anyone, we successfully mocked out
LayoutAnimation
using the code belowThough, none of the above worked for me, they really helped to find a solution. I was eventually able to properly mock the individual modules by following the implementation described in this blog post.
So, as @ide, suggested, I mocked the react-native interface. Using
jest.mock()
insidesetup.js
did not work for me, but instead I created areact-native.js
file intests/__mocks__
and added exports for the modules I need to mock:Also, this allowed me to easily mock platform detection by simply overwriting the
Platform.OS
property inside a test (inspired by @tjbenton’s answer):And, to check that the mocked methods are called as expected:
Hi all! After applying any and all of workarounds proposed here I still face the issue with these errors:
Any clue how to resolve it?
I got same error as @0akl3y people think it’s just easy to mock react-native but it’s not! So please stop giving us those boilerplate answers.
I’ve found a workaround for this issue. You can add all
Library
subdirectories of react-native to themoduleDirectories
prop in yourjest.config.js
. I’ve done this for our app with a recursive file-walker function:After that, all mocks resolve correctly.
https://jestjs.io/docs/en/configuration#moduledirectories-array-string
I’m aware it’s definitely not the best solution and everyone should be aware that if they use it it could break at any time but for now it seems to be the only way to get the tests running on the new React Native version. We tried to mock all of React Native the way we’re currently mocking out Layout Animation but it didn’t seem to work properly…
If anyone manages to successfully create a mock it would be great if they could share it here.
@ccfz that does not work for me. Not sure if this is what you meant, but here is what I tried:
results in an error:
I also tried it with
require.requireActual(...)
with the same result. So far the only viable solution seems to be importing the react-native modules via the full path (Which is -like mentioned before- not stable)Thank you for looking into this
@ide That does not work for component getters, since it seems like those cannot be overwritten that way. However there is a way around this by using
Object.defineProperty
:Test results:
The snapshot also renders the mock correctly:
The following article pointed me in the correct direction: https://bambielli.com/til/2017-09-17-jest-doesnt-mock-getters/
However this seems like a workaround and requires much refactoring. I would prefer if it just worked like before. Publishing private implementation details seems to be the lesser evil than to lose the capability to conveniently mock those dependencies.
@ccfz You’d mock out react-native and re-export it with a different value for
Animated
. For example,mockReactNative = { ...originalReactNative, Animated: { whatever } }
. Similar to how you’re mocking out just Animated.timing, you would mock out just ReactNative.Animated. Same thing for NativeModules, etc.This is a working example with 0.61.2: https://github.com/facebook/react-native/issues/26579#issuecomment-538610849
@lroling8350 Mocking via the direct path is definitely a way to make this work. The problem with that is when RN changes the file path like @ide pointed out above. Checking if Keyboard’s
dismiss
was called can also be tested using a spy if someone just wants to do that.However, I have more complicated mocks related to Animated and TextInput that require actual mocking. @fossage I ran into the same problem trying to mock out react-native. @ide could you maybe go into a little more detail about how something like jest.mock(‘Animated’) should be mocked with 0.61.1? It’s a common mock that requires an overriding aspect of Animated. Here is a sample mock I took from here, but we have very similar code in our company’s app.
This seems to be the issue with the most details related to mocking in RN 0.61.1, @fossage maybe re-open the issue for now?
The easiest way I found to mock out the part that I needed was to just import it and overwrite it.
Previously I had this, and now it fails in 0.61
To fix the issue in 0.61
@fossage Could you please show what you solution ended up being?
In my use case I want to check that Keyboard’s
dismiss
was called. So far I can just do:Mocking out
react-native
would mean something like this right?which works to mock the Keyboard, but obviously the other react-native libraries, eg. Image are undefined now.
Did you add the other libraries to the mock? or what was your solution for this?
Mocking React Native’s interface did not work for me, as @altany suggested. I created a
setup.js
and added it tosetupFilesAfterEnv
config option:Then, in each test, you can change
Platform
’s implementation like so:This looks reasonable – due to the way RN has worked for a long time,
Object.defineProperty
is one of the few ways to override a getter (Object.setPrototypeOf({ Button: 'MockedButton' }, ReactNative)
being another). I updated the code sample above so the examples are in one place.For a given version of RN, code like this will work:
It is a more brittle approach because of the coupling between your test and the internal path. If you understand it introduces coupling and can fix the tests if needed in the future, this approach might work for you. But in general with Jest, mocking the public interface you expect to interact with is a more robust approach and more likely to last.
@mksglu thanks! 🙇 🙏 I’ll take note of this approach and try to make it work with 0.63.4 on my end.
@SophieDonut Thank you for the example! A caveat with your solution is that the path to the library is a private implementation and therefore not stable. If RN changes the path to the libraries then you would have to update all your mocks. See @ide comment at the top.
This is intentional and you need to mock modules the same way as any other JS module now. You could in theory specify the path to the
TextInput
module, but the path is a private implementation detail that could change between releases. The logically correct approach is to code to the interface and mock outreact-native
.