Jest tests broken with v4
This section of the docs is outdated: Testing with Jest or Vitest. Running unit tests with Jest (node test environment), after upgrading, leads to the following errors:
Failed to get NitroModules: The native "NitroModules" Turbo/Native-Module could not be found.
* Make sure react-native-nitro-modules/NitroModules is correctly autolinked (run `npx react-native config` to verify)
* Make sure you enabled the new architecture (TurboModules) and CodeGen properly generated the "NativeNitroModules"/NitroModules specs. See https://github.com/reactwg/react-native-new-architecture/blob/main/docs/enable-apps.md
* Make sure you are using react-native 0.75.0 or higher.
* Make sure you rebuilt the app.
* Make sure you ran `expo prebuild`.
* Make sure you ran `pod install` in the ios/ directory.
at Object.<anonymous> (node_modules/react-native-nitro-modules/src/turbomodule/NativeNitroModules.ts:20:9)
at Object.require (node_modules/react-native-nitro-modules/src/NitroModules.ts:2:1)
at Object.require (node_modules/react-native-nitro-modules/src/index.ts:2:1)
at Object.require (node_modules/react-native-mmkv/lib/createMMKV/createMMKV.js:1:1)
at Object.require (node_modules/react-native-mmkv/lib/index.js:2:1)
Also, this no longer exists: https://github.com/mrousavy/react-native-mmkv/blob/main/example/tests/MMKV.test.ts.
Guten Tag, Hans here.
[!NOTE] New features, bugfixes, updates and other improvements are all handled mostly by
@mrousavyin his free time. To support@mrousavy, please consider 💖 sponsoring him on GitHub 💖. Sponsored issues will be prioritized.
PRs welcome - I don't use jest so I don't know a good answer to this.
Here is the mock MMKV I added to our project for unit tests:
// Mock for react-native-mmkv
// In-memory storage implementation for MMKV interface
export class MockMMKV {
private storage: Map<string, boolean | string | number | ArrayBuffer>
private listeners: Set<(changedKey: string) => void>
constructor() {
this.storage = new Map()
this.listeners = new Set()
}
set(key: string, value: boolean | string | number | ArrayBuffer): void {
this.storage.set(key, value)
this.notifyListeners(key)
}
getString(key: string): string | undefined {
const value = this.storage.get(key)
return typeof value === "string" ? value : undefined
}
getNumber(key: string): number | undefined {
const value = this.storage.get(key)
return typeof value === "number" ? value : undefined
}
getBoolean(key: string): boolean | undefined {
const value = this.storage.get(key)
return typeof value === "boolean" ? value : undefined
}
getBuffer(key: string): ArrayBuffer | undefined {
const value = this.storage.get(key)
return value as ArrayBuffer | undefined
}
getAllKeys(): string[] {
return Array.from(this.storage.keys())
}
recrypt(_key: string | undefined): void {
throw new Error("Method not implemented.")
}
remove(key: string): boolean {
const result = this.storage.delete(key)
this.notifyListeners(key)
return result
}
contains(key: string): boolean {
return this.storage.has(key)
}
clearAll(): void {
const keys = Array.from(this.storage.keys())
this.storage.clear()
keys.forEach((key) => this.notifyListeners(key))
}
addOnValueChangedListener(listener: (changedKey: string) => void): { remove: () => void } {
this.listeners.add(listener)
return {
remove: () => {
this.listeners.delete(listener)
},
}
}
private notifyListeners(key: string): void {
this.listeners.forEach((listener) => listener(key))
}
}
and in jest.setup.ts:
jest.mock("react-native-mmkv", () => ({
__esModule: true,
MMKV: MockMMKV,
createMMKV: jest.fn(() => new MockMMKV()),
}))
to create an mmkv instance, you should mock the createMMKV function.
const mockStorage = new Map();
return {
createMMKV: () => ({
set: (key, value) => {
mockStorage.set(key, value);
},
getString: (key) => {
const value = mockStorage.get(key);
return typeof value === 'string' ? value : undefined;
},
getNumber: (key) => {
const value = mockStorage.get(key);
return typeof value === 'number' ? value : undefined;
},
getBoolean: (key) => {
const value = mockStorage.get(key);
return typeof value === 'boolean' ? value : undefined;
},
remove: (key) => {
mockStorage.delete(key);
},
clearAll: () => {
mockStorage.clear();
},
contains: (key) => {
return mockStorage.has(key);
},
getAllKeys: () => {
return Array.from(mockStorage.keys());
},
}),
};
});```
There is an answer in nitro repository https://github.com/mrousavy/nitro/issues/812#issuecomment-3425515066
There is createMockMMKV
@zatteo This did not work for me:
jest.mock('react-native-nitro-modules', () => {
return {
NitroModules: () => {
return {};
},
};
});
I mocked mine like this, no issue so far.
export const createMMKV = () => ({
getString: jest.fn(),
set: jest.fn(),
delete: jest.fn(),
});
Adding the following before running my Jest tests was enough:
jest.mock("react-native-nitro-modules", () => {
return {
createHybridObject: jest.fn(() => {
// Return a mock object that won't be used since MMKV has its own mock
return {}
}),
}
})
we might need to update/fix the mock here to contain the mock i posted above: https://github.com/mrousavy/react-native-mmkv/blob/main/packages/react-native-mmkv/mocks/react-native-nitro-modules.js