-
Notifications
You must be signed in to change notification settings - Fork 476
[feat] Jest mocks, AsyncStorage testing guidelines #53
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Looks like a good start, but we can make it better :) Left notes inlined
docs/Jest-integration.md
Outdated
|
||
import AsyncStorageMock from '@react-native-community/async-storage/jest/async-storage-mock'; | ||
|
||
jest.doMock('react-native', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to use jest.mock
, because it's more widely used
docs/Jest-integration.md
Outdated
2. Inside that folder, create `react-native.js` file. | ||
3. Inside that file, import `AsyncStorageMock` and use it to mock `NativeModules`. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI you can run Prettier on .md files to remove excess whitespace :)
docs/Jest-integration.md
Outdated
// other config | ||
"setupFiles": "./path/to/jestSetupFile.js" | ||
}, | ||
// ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a valid JSON. Also setupFiles
accepts an array. Please fix to:
{
"jest": {
"setupFiles": ["./path/to/jestSetupFile.js"]
}
}
docs/Jest-integration.md
Outdated
```json | ||
"jest": { | ||
// ... | ||
"transformIgnorePatterns": ["/node_modules/@react-native-community/async-storage/(?!(lib))"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Make it valid JSON
- Use a catch-all regex for all
@react-native-community
packages to help others :D :
"transformIgnorePatterns": ["node_modules/(?!react-native|@react-native-community)"]
- Make a note that this should be included by default in RN 0.60
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see this addressed, mind fixing?
jest/async-storage-mock.js
Outdated
/** | ||
* Clears all keys in storage | ||
*/ | ||
clear: (cb: CallbackType) => cb(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the mock is actually test-framework agnostic. Maybe it's nice, but I think most of the users would expect this mock to be fully functional in JS-land only, plus it would be nice if it used jest.fn
to mock methods, because that would make them, well, mocks :P which you can inspect.
How about getting rid of the example mock and just provide the basic implementation + mocks here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my dilemma here - to provide fully functional mock or simple one + docs about providing your own functionality. I decided to do the latter, as I thought most people would use it to pass tests.
But I think you might be right here - it's much easier for users to implement it other way around. Going to make it fully functional first.
Also, I had jest.fn
at first, but then I thought about accessing this mock in your test - how would you do that? You'd have to import mock into your test and check, i.e if it was called with such arguments. Or am I missing something here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you substitute module with a mock like RN.AsyncStorage = ASMock;
, AsyncStorage
should be a mock then and you'll be able to do expect(AsyncStorage.getItem).toBeCalledWith(smth)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but this is mock for NativeModule
, not something that user has access too
lib/AsyncStorage.js
Outdated
|
||
To fix this issue try these steps: | ||
• Run \`react-native link @react-native-community/async-storage\` in the project root. | ||
• Rebuild and re-run the app. | ||
• Restart the packager with \`--clearCache\` flag. | ||
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app. | ||
• If this error shows up while testing with Jest, check out how to mock Async Storage in repository docs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be nice to provide a link
package.json
Outdated
@@ -54,7 +54,8 @@ | |||
"semantic-release": "15.13.3" | |||
}, | |||
"jest": { | |||
"preset": "react-native" | |||
"preset": "react-native", | |||
"testMatch": [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(test).[jt]s?(x)" ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks pretty much lika a default testMatch, do you really need to change it?. Also, format with Prettier
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I left .spec
out, for e2e tests. Going to change that.
@thymikee All of public facing methods of the mock is not Also, added unit test to check mock itself, for both Promise and Callback functionality. Thanks for your review so far and please let me know what you think 🙏 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Feel free to merge after the nits are addressed :)
docs/Jest-integration.md
Outdated
|
||
Select a method that suits your needs: | ||
|
||
### Mock `node_modules` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to With __mocks__ directory
docs/Jest-integration.md
Outdated
@@ -0,0 +1,77 @@ | |||
# Jest integration | |||
|
|||
Async Storage module is tighly coupled with a `Native Module`, meaning it needs a running React Native application to work properly. In order to use it in tests, you have to provide its separate implementation. Follow those steps to add mocked `Async Storage` to your test cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Native Module
-> NativeModule
docs/Jest-integration.md
Outdated
Each public method available from `Async Storage` is [a mock function](https://jestjs.io/docs/en/mock-functions), that you can test for certain condition, for example, check if it has been called with a specific arguments: | ||
|
||
```javascript | ||
it('checks if Async Storage is used', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wonder why the indentation? Are you using Prettier to format that? If not, I definitely recommend
docs/Jest-integration.md
Outdated
```json | ||
"jest": { | ||
// ... | ||
"transformIgnorePatterns": ["/node_modules/@react-native-community/async-storage/(?!(lib))"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see this addressed, mind fixing?
expect(value1).toBeNull(); | ||
expect(value2).toBeNull(); | ||
done(); | ||
}).catch(e => done.fail(e)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're using callback approach, but here taking advantage of getItem
returning a promise. You should use the first argument (named _
instead of err
or error
) and check if it's not null, then call done.fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason why I'm catching some of the block is that error
thrown in callback methods are swallowed by Jest - meaning it'll get timeout
singal, without specific reason why caused that.
Note that I only wrap blocks with assertions inside - I couldn't find nicer way to do callbacks
and still get errors when assertion fail
docs/Jest-integration.md
Outdated
|
||
```json | ||
"jest": { | ||
"transformIgnorePatterns": ["node_modules/(?!(@react-native-community))/"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"transformIgnorePatterns": ["node_modules/(?!(@react-native-community))/"] | |
"transformIgnorePatterns": ["node_modules/(?!(react-native|@react-native-community))"] |
🎉 This PR is included in version 1.3.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary:
We had some issues with Async Storage when it comes to testing with Jest. This PR adds mocks to be used while testing, to avoid error and integrate AS better.
Also some updates to README/Error thrown when module is not linked.
Small fix for CI job.
This PR will close:
#51, #39, #12
Test Plan:
Green CI, test it with an app.