spyOn #react-native-firebase/analytics methods - jestjs

Basically, I want to make sure the methods of analytics are called with certain properties but so far it is not working:
Cannot spy the logAppOpen property because it is not a function; undefined given instead
the library is successfully mocked since I can see console log out of my jest.fn():
jest.mock('#react-native-firebase/analytics', () => {
return () => ({
logAppOpen: jest.fn(() => console.log('mocked fun called')), //===>shown correctly
})
})
My class is:
import analytics from '#react-native-firebase/analytics';
export default class GA {
appStarted = async () =>{
console.log('appStarted called'); //==> showing
await analytics().logAppOpen();
}
}
my test:
it("should log app starting", async () =>{
const spy = jest.spyOn(analytics, 'logAppOpen') //===>FAILS HERE
congst ga = new GA();
await ga.appStarted();
expect(spy).toHaveBeenCalled();
})
but in my test: console.log(analytics) does show an empty object {}

It's analytics().logAppOpen() while jest.spyOn tries to spy on analytics.logAppOpen which doesn't exist.
For lazily evaluated spied functions it's easier to expose them as variables:
const mockLogAppOpen = jest.fn();
jest.mock('#react-native-firebase/analytics', () => {
return jest.fn()
.mockReturnValue({ logAppOpen: mockLogAppOpen });
});
This way it can be accessed for call assertions. There's no need for jest.spyOn for a function that is already a spy.

Related

How to mock a function from the same module as the function being tested

So, I have two methods on a Node project:
export function methodA() {
const response = await methodB();
return response.length ? 'something' : 'else';
}
export function methodB() {
const array = await getData(); // Access database and make API calls
return array[];
}
methodA calls methodB and methodB makes stuff that I don't care right now for my unit testing purposes, so I want to mock methodB so it will return an empty array and won't try to make any database or API calls. The issue is that I can't actually mock methodB, as my test is still calling the actual function.
Here's my test:
describe('method testing', () => {
it('calls method', async () => {
const response = await myModule.methodA();
expect(response).toBe('else');
});
});
That test is failing, because jest is still calling the actual methodB which is meant to fail, as it can't connect to the database or reach APIs, so, this is what I tried doing to mock methodB:
Spying on the method:
import * as myModule from '#/domains/methods';
jest.spyOn(myModule, 'methodB').mockImplementation(() => [] as any);
// describe('method testing', () => {...
Mocking the entire file except for the methodA:
jest.mock('#/domains/methods', () => {
const originalModule = jest.requireActual('#/domains/methods')
return {
...originalModule,
methodB: jest.fn().mockReturnValue([])
}
});
// describe('method testing', () => {...
I have also tried:
Mocking methodB inside each test and inside describe
Spying methodB inside each test and inside describe
Some variations of those examples I wrote above
I'm not entirely sure on what to do right now, so any light would be appreciated.
#Update: Altough the problem is similar, this is not a duplicate question and this (How to mock functions in the same module using Jest?) does not answer my question.

What is the right way to mock and unmock 'node-fetch' module in Node.js using Jest and Typescript?

Testing function
import fetch from 'node-fetch';
class Services () {
someFunc() {
const data = await fetch(dummy url);
// business logic
}
anotherFunc() {
const data = await fetch(dummy url);
// business logic
}
}
export default new Service();
Actual testing
import fetch from 'node-fetch';
jest.mock('node-fetch', () => jest.fn());
beforeEach(() => {
mocked(fetch).mockClear();
})
describe('test first method', () => {
it('should fetch some data without mock fetch', async () => {
const data = await service.someFunc();
expect(data).toHaveProperty('items');
})
})
describe('test second method', () => {
it('should return data with mock fetch', async () => {
mocked(fetch).mockImplementationOnce(() => Promise.reject());
const data = await service.anotherFunc();
expect(data).toHaveProperty('items');
})
})
At this moment I have a problem with the fetch functions in the first describe case and get " TypeError: Cannot read property 'then' of undefined". So I need a mocked version of fetch only in the second describe case. In the first describe case it should use fetch without mock function replacement.
How I can fix it? Will be grateful for the assist.

Unmock function after mockimplementation

I'm having a bit of trouble unmocking a function.
I first mock it and now I can't unmock it
//myClass.js
class myClass {
static check(v1,v2) {
return v1 > v2;
}
static async getinfo(v1,v2) {
if (this.check(v1,v2)) {
return await get('api.google.com');
}
return [];
}
}
//myclass.spec.js
describe('Testing myClass', () => {
describe('testing processing', () => {
it('should return result', () => {
const mockPatch = jest.fn().mockImplementation((version, solution) => false);
myClass.check = mockCheck;
try {
const result = await myClass.getinfo(1,2);
expect(result).toBe.([]);
}catch(e) {
throw e;
}
})
})
describe('Testing check', () => {
it('should return true', () => {
expect(myClass.check(2,1)).toBe.true
})
})
})
I already try with
myClass.check.mockRestore()
beforeEach(() => {myClass.check.mockRestore()})
jest.unmock('./myClass.js)
Is there anyway I can solve this? I read all the jest doc and i couldn't find anything
Methods should never be mocked by reassigning them, there is no way how Jest could restore their original implementation this way.
This should always be done with spyOn:
jest.spyOn(myClass, 'check').mockReturnValue(false)
This way a method can be restored with restoreMock or restoreAllMocks. This should be preferably enabled globally in Jest configuration.
I'm assuming that what you're hoping to do is to mock an implementation for use in a specific test, but then have your other tests function without the mocking.
If so, I think you could use the module mocking strategy in conjunction with mockReturnValueOnce.
Be sure to import your module at the top of your tests, then to call jest.mock with the same path. After that, you should be able to call myClass.check.mockReturnValueOnce, and it will be mocked until the next time it is called. After that, it will function normally 👍

How to use resetMocks:true with module factory mocks

I would like to mock imported modules while keeping my unit tests independent of each other.
Setting resetMocks:true in my Jest config file means that behaviour set up using module factory mocking is lost (issue). Setting up module mocks in any other way doesn't work (issue).
Changing to resetMocks:false couples the unit tests and makes the order they are executed matter, which goes against unit testing best practices.
I have tried calling jest.mock('./a', () => {/* implementation */}) inside beforeEach() and at the top of test(). I have also tried to use a reference to a jest.fn() inside the module factory mock and then call .mockImplementation() on that reference.
Minimum demonstration:
// greeter.ts
export class Greeter {
sayHello(): string {
return 'hello world!';
}
}
// module-mocking.spec.ts
import { Greeter } from "./greeter";
jest.mock('./greeter', () => ({
Greeter: jest.fn(() => ({ sayHello: () => 'goodbye world!' }))
}));
test('mocked module behaviour should exist', () => {
const result = new Greeter().sayHello();
expect(result).toEqual('goodbye world!');
});
This test fails with the error:
TypeError: (intermediate value).sayHello is not a function
Moving the jest.mock() inside beforeEach() or into test() results in:
Expected: "goodbye world!" Received: "hello world!"
Edit
I managed to work around this by using require instead of import. The question still remains for ES6 imports.
// module-mocking.spec.ts
const greeter = require("./greeter");
let mockGreeter: any;
beforeEach(() => {
mockGreeter = { sayHello: () => 'goodbye world!' };
greeter.Greeter = jest.fn(() => mockGreeter);
});
test('mocked module behaviour should exist', () => {
const result = new Greeter().sayHello();
expect(result).toEqual('goodbye world!');
});

Accessing .mock property of an automocked function

I have this code:
import * as a from 'a-a';
jest.mock('a-a');
describe('a-a', () => {
beforeAll(async () => {
const x = await a.x(1); // Calls the mock
console.log(x); // 1
console.log(a.x.mock) // Undefined
});
});
The mock function is:
export async function x(data) {
cache.push(data);
console.log('HERE'); // this is printed
return data;
}
The mock of the module is in the __mocks__ directory.
The a.x() calls the mocked function, but a.x.mock is undefined.
How is that possible? Where is the .mock property?
So, after some investigation I found out that the functions declared in the __mocks__ directory aren't wrapped by jest.fn() by default.
Personally I find the thing a bit confusing.
So you can do both
function x(data) {
cache.push(data);
return cache;
}
jest.mock('a-a', () => ({x: x}))
if you do everything in the same file, or
jest.mock('a-a');
and then in the __mocks__/a-a.js file
export const x = jest.fn(async (data) => {
cache.push(data);
return cache;
});

Resources