-
Notifications
You must be signed in to change notification settings - Fork 24.4k
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
0.61.0 - Jest "Cannot find module ..." #26579
Comments
Similar to: #26447 |
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 |
@fossage Could you please show what you solution ended up being? In my use case I want to check that Keyboard's
Mocking out
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? |
@ccfz , unfortunately I was just assuming that I could get this working without having actually tried. I never was able to figure out a solution, specifically due to the problem you are describing. The things I did try to no avail were:
jest.mock('react-native', () => {
const actualRN = require.requireActual('react-native')
return {
...actualRN,
Keyboard: {
...
}
}
}) This failed because we would end up getting the actual version of RN rather than the mocked version created by the Ultimately I decided to fallback to RN 0.60.6 until some solution is found:(. |
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. |
@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 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. jest.mock('Animated', () => {
const ActualAnimated = require.requireActual('Animated');
return {
...ActualAnimated,
timing: (value, config) => {
return {
start: (callback) => {
value.setValue(config.toValue);
callback && callback()
},
};
},
};
}); 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? |
Yes, @fossage please re-open this issue. We too have not yet been able to upgrade to 0.61 because of the mocking issues. This is how we mock
Which raises the following error:
|
Same issue here nothing is working using latest rn 0.61.1 |
In case this is helpful for anyone, we successfully mocked out jest.mock("react-native/Libraries/LayoutAnimation/LayoutAnimation", () => ({
...require.requireActual(
"react-native/Libraries/LayoutAnimation/LayoutAnimation"
),
configureNext: jest.fn(),
})); |
@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. |
Mocking something like the libraries and components is fine when you point to the component directly as above, but when trying to mock something like BackHandler or TouchableOpacity it seems to throw an issue. These mocks come from the @types folder and cant be mocked seemingly. |
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. |
We use Not being able to do such is preventing our upgrade to 0.61.x. |
@ccfz You'd mock out react-native and re-export it with a different value for This is a working example with 0.61.2: #26579 (comment) |
@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 Thank you for looking into this |
Have you been able to successfully mock the TouchableOpacity or the BackHandler? As I have not been able to just yet. |
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. |
@ide I think the issue here is that doing what you are proposing does not work. Doing so will lead to the error that @oakl3y pointed to earlier. Many of us on the thread have tried it, and numerous other variations, and haven't come up with any viable solution. I still have a few things I would like to try, however I have a feeling that this is something that will need to be addressed by the RN team. One of the main problems(aside from the error) about doing something like: jest.mock('react-native', () => {
return {
...require.requireActual('react-native'),
[...specific module mocks]
}
}) is that the call to |
@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
|
@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
Test results:
The snapshot also renders the mock correctly:
The following article pointed me in the correct direction: 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. |
This looks reasonable -- due to the way RN has worked for a long time,
For a given version of RN, code like this will work: jest.doMock('react-native/Libraries/Components/Button', () => 'MockedButton'); 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. |
I've found a workaround for this issue. You can add all // jest.config.js
const fs = require('fs')
const path = require('path')
function getFolderList(rootDir) {
let files = fs.readdirSync(rootDir)
files = files.map(file => {
const filePath = path.join(rootDir, file)
const stats = fs.statSync(filePath)
if (stats.isDirectory()) return getFolderList(filePath)
else if (stats.isFile()) return path.dirname(filePath)
})
const fileList = files.reduce((all, folderContents) => all.concat(folderContents), [])
const folderList = fileList.filter((filePath, index) => fileList.indexOf(filePath) === index)
return folderList
}
const moduleDirectories = [
'node_modules',
...getFolderList(__dirname + '/node_modules/react-native/Libraries'),
]
module.exports = {
moduleDirectories,
// ... all your other jest.config.js configurations
} After that, all mocks resolve correctly. https://jestjs.io/docs/en/configuration#moduledirectories-array-string |
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.61jest.mock('Animated', () => {
const ActualAnimated = require.requireActual('Animated')
return {
...ActualAnimated,
timing: (value, config) => {
return {
start: (callback) => {
value.setValue(config.toValue)
callback?.({ finished: true })
},
}
},
}
}) To fix the issue in 0.61 import { Animated } from 'react-native'
Animated.timing = (value, config) => {
return {
start: (callback) => {
value.setValue(config.toValue)
callback?.({ finished: true })
},
}
} |
Hi all!
Any clue how to resolve it? |
Hi @G0retZ just the error message makes it hard to pinpoint the issue. Could you please show the mocking you did and or test. That way we can help you figure out what went wrong. |
That was a good solution ;) cheers. |
same issue here. does this issue caused by auto linking not applied to jest ?.. so what's the official solution... |
Though, 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 import * as ReactNative from "react-native";
export const alert = jest.fn();
export const Alert = { alert };
export const dimensionWidth = 100;
export const Dimensions = {
get: jest.fn().mockReturnValue({ width: dimensionWidth, height: 100 })
};
export const Image = "Image";
export const keyboardDismiss = jest.fn();
export const Keyboard = {
dismiss: keyboardDismiss
};
export const Platform = {
...ReactNative.Platform,
OS: "ios",
Version: 123,
isTesting: true,
select: objs => objs["ios"]
};
export default Object.setPrototypeOf(
{
Alert,
Dimensions,
Image,
Keyboard,
Platform
},
ReactNative
); Also, this allowed me to easily mock platform detection by simply overwriting the import { Platform } from "react-native";
it('renders Element if Android', () => {
Platform.OS = 'android'
...
}) And, to check that the mocked methods are called as expected: import { alert } from "react-native";
it("showAlert() calls Alert.alert", () => {
showAlert();
expect(alert).toHaveBeenCalled();
}); |
@ccfz for me,that solution( #26579 (comment) ) caused other issues:
I removed all node_modules , cleaned npm cache, cleaned pod cache, then pod install. instead of mocking |
Mocking React Native's interface did not work for me, as @altany suggested. I created a jest.mock('react-native/Libraries/Utilities/Platform', () => ({
OS: 'ios',
select: jest.fn((selector) => selector.ios),
})); Then, in each test, you can change import { Platform } from 'react-native';
test('your test', () => {
Platform.select.mockImplementation((platforms) => platforms.android);
Platform.OS = 'android';
...
}); |
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 React Native [1], also means we're better prepared for the React Native v0.61 upgrade (zulip#3781), in which Haste is removed [2]. A consequence of that removal, it seems, is that mocks like this one, which we have now: ``` jest.mock('Linking', () => { ... }` ``` , won't work. Several people have handled this by changing 'Linking' to something like 'react-native/Libraries/Linking/Linking', but this is brittle because it couples our tests with the current directory structure in 'react-native'. Better to do it this way. We considered following the advice of others at that issue, including a blog post [3] responding to the official suggestion with an alternative. But we didn't reproduce the problems the post's author mentioned, and we've so far been able to explain the hiccups we've seen. [1] facebook/react-native#26579 (comment) [2] facebook/react-native#26579 (comment) [3] facebook/react-native#26579 (comment)
Doing this, as recommended by React Native [1], means we're better prepared for the React Native v0.61 upgrade (zulip#3781), in which Haste is removed [2]. A consequence of that removal, it seems, is that mocks like this one, which we have now: ``` jest.mock('Linking', () => { ... }` ``` , won't work. Several people have handled this by changing 'Linking' to something like 'react-native/Libraries/Linking/Linking', but this is brittle because it couples our tests with the current directory structure in 'react-native'. Better to do it this way. We considered following the advice of others at that issue, including a blog post [3] responding to the official suggestion with an alternative. But we didn't reproduce the problems the post's author mentioned, and we've so far been able to explain the hiccups we've seen. [1] facebook/react-native#26579 (comment) [2] facebook/react-native#26579 (comment) [3] facebook/react-native#26579 (comment)
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', added in a recent commit, rather than pointing to 'react-native/Libraries/Linking/Linking' and thus coupling our tests with the internal directory structure of `react-native`. [1] [1]: See React Native's recommendation of this strategy, at facebook/react-native#26579 (comment).
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 React Native [1], also means we're better prepared for the React Native v0.61 upgrade (zulip#3781), in which Haste is removed [2]. A consequence of that removal, it seems, is that mocks like this one, which we have now: ``` jest.mock('Linking', () => { ... }` ``` , won't work. Several people have handled this by changing 'Linking' to something like 'react-native/Libraries/Linking/Linking', but this is brittle because it couples our tests with the current directory structure in 'react-native'. Better to do it this way. We considered following the advice of others at that issue, including a blog post [3] responding to the official suggestion with an alternative. But we didn't reproduce the problems the post's author mentioned, and we've so far been able to explain the hiccups we've seen. [1] facebook/react-native#26579 (comment) [2] facebook/react-native#26579 (comment) [3] facebook/react-native#26579 (comment)
Doing this, as recommended by React Native [1], means we're better prepared for the React Native v0.61 upgrade (zulip#3781), in which Haste is removed [2]. A consequence of that removal, it seems, is that mocks like this one, which we have now: ``` jest.mock('Linking', () => { ... }` ``` , won't work. Several people have handled this by changing 'Linking' to something like 'react-native/Libraries/Linking/Linking', but this is brittle because it couples our tests with the current directory structure in 'react-native'. Better to do it this way. We considered following the advice of others at that issue, including a blog post [3] responding to the official suggestion with an alternative. But we didn't reproduce the problems the post's author mentioned, and we've so far been able to explain the hiccups we've seen. [1] facebook/react-native#26579 (comment) [2] facebook/react-native#26579 (comment) [3] facebook/react-native#26579 (comment)
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', added in a recent commit, rather than pointing to 'react-native/Libraries/Linking/Linking' and thus coupling our tests with the internal directory structure of `react-native`. [1] [1]: See React Native's recommendation of this strategy, at facebook/react-native#26579 (comment).
With something called Haste, we were allowed to just pass "Linking" to `jest.mock`, and it would automagically be known that we want something called "Linking" from React Native. With RN v0.61, Haste is no longer used, and that way of mocking breaks. One possible response is to spell out the entire path to "Linking" within `react-native`: jest.mock('react-native/Libraries/Linking/Linking') . But that's brittle: that path may change with new React Native versions, and it'll be unpleasant to have to adapt. The recommended solution [1] is to mock the `react-native` module ourselves, on top of the mocking that React Native's Jest setup does for us. And to put our "Linking" mock there. So, do. The *exact* recommendation is something that uses `Object.setPrototypeOf`. We don't do that. Instead, Greg found an earlier revision of the comment where that recommendation appears, and go from there. This way, we avoid an awkward problem with `react-native-vector-icons`. That library re-exports `react-native` in its `lib/react-native.js`, and imports that when they want properties from the `react-native` module. Errors ensue; it appears that their strategy cuts off access to properties we'd intended to make available by using ReactNative as a prototype. So, don't mess around with prototypes. [1] facebook/react-native#26579 (comment)
With something called Haste, we were allowed to just pass "Linking" to `jest.mock`, and it would automagically be known that we want something called "Linking" from React Native. With RN v0.61, Haste is no longer used, and that way of mocking breaks. One possible response is to spell out the entire path to "Linking" within `react-native`: jest.mock('react-native/Libraries/Linking/Linking') . But that's brittle: that path may change with new React Native versions, and it'll be unpleasant to have to adapt. The recommended solution [1] is to mock the `react-native` module ourselves, on top of the mocking that React Native's Jest setup does for us. And to put our "Linking" mock there. So, do. The *exact* recommendation is something that uses `Object.setPrototypeOf`. We don't do that. Instead, Greg found an earlier revision of the comment where that recommendation appears, and go from there. This way, we avoid an awkward problem with `react-native-vector-icons`. That library re-exports `react-native` in its `lib/react-native.js`, and imports that when they want properties from the `react-native` module. Errors ensue; it appears that their strategy cuts off access to properties we'd intended to make available by using ReactNative as a prototype. So, don't mess around with prototypes. [1] facebook/react-native#26579 (comment)
With something called Haste, we were allowed to just pass "Linking" to `jest.mock`, and it would automagically be known that we want something called "Linking" from React Native. With RN v0.61, Haste is no longer used, and that way of mocking breaks. One possible response is to spell out the entire path to "Linking" within `react-native`: jest.mock('react-native/Libraries/Linking/Linking') But that's brittle: that path may change with new React Native versions, and it'll be unpleasant to have to adapt. The recommended solution [1] is to mock the `react-native` module ourselves, on top of the mocking that React Native's Jest setup does for us. And to put our "Linking" mock there. So, do. The *exact* recommendation is something that uses `Object.setPrototypeOf`. We don't do that. Instead, Greg found an earlier revision of the comment where that recommendation appears, and we go from there. This way, we avoid an awkward problem with react-native-vector-icons. That library re-exports `react-native` in its `lib/react-native.js`, and imports that when they want properties from the `react-native` module. Errors ensue; it appears that their strategy cuts off access to properties we'd intended to make available by using ReactNative as a prototype. So, don't mess around with prototypes. [1] facebook/react-native#26579 (comment)
I tried all the solutions above in React Native version 0.63.4, but that does not work I want to changes the Platform's implementation in each test. cc @altany @acostalima
|
@mksglu I'm yet to try the approach I suggested before in 0.63.4. Did you manage to find a solution meanwhile? |
Hello @acostalima, thank you for the response. I guess finally found a solution for 0.63.4. I created a new file in import * as ReactNative from 'react-native';
jest.doMock('react-native', () => {
return Object.setPrototypeOf(
{
Dimensions: {
get: () => ({width: 414, height: 896}),
},
Platform: {
...ReactNative.Platform,
OS: 'ios',
select: jest.fn((selector) => selector),
},
NativeModules: {
...ReactNative.NativeModules,
SettingsManager: {
settings: {
AppleLocale: 'en_US',
},
},
I18nManager: {
localeIdentifier: 'en_US',
},
},
},
ReactNative,
);
}); Then, when I create a test file like import {Platform} from 'react-native';
describe('Button', () => {
it('should call onPress function', () => {
Platform.OS = 'android';
});
}); |
@mksglu thanks! 🙇 🙏 I'll take note of this approach and try to make it work with 0.63.4 on my end. |
After upgrading from
0.60.5
to0.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: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(R) Core(TM) 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
jest.mock('View')
Describe what you expected to happen:
Jest should fail with an error telling you
Cannot find module 'View' ...
The text was updated successfully, but these errors were encountered: