Jest spy on a function inside a custom hook [duplicate] - jestjs

This question already has answers here:
Mocking React custom hook with Jest
(3 answers)
Closed 7 months ago.
I have a custom hook called useCustom, that implements a function called doSomething.
In my react component I want to spy on the doSomething that it has been called with certain values. But it never gets called. What would be the proper way to mock or spy on that doSomething method?
This is what I tried:
myComponent.tsx
...
const { doSomething} = useCustom();
doSomething('a','b');
...
useCustom.ts
export const useCustom= () => {
const doSomething = (param1, param2): void => {
...
};
return { doSomething };
myComponent.spec.ts
it('should call dosomething', () => {
const doSomethingSpy= jest.spyOn(useCustom(), 'doSomething');
...
expect(doSomethingSpy).toHaveBeenCalledWith('a','b');
});

I now managed to do it like this:
import * as useCustomHook from '#hooks/useCustom';
it('should call dosomething', () => {
const doSomethingMock= jest.fn();
jest.spyOn(useCustomHook, 'useCustom').mockImplementation(() => {
return {
doSomething: doSomethingMock,
};
});
...
expect(doSomethingMock).toHaveBeenCalledWith('a','b');
});

Related

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 👍

spyOn #react-native-firebase/analytics methods

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.

Spy on imported function

I want to spy on a function that is executed as soon as the file is required. In the example below I want to spy on bar. I have the following files.
code.ts
import {bar} from 'third-party-lib';
const foo = bar()
test.ts
import * as thirdParty from 'third-party-lib';
describe('test', () => {
let barStub: SinonStub;
beforeEach(() => {
barStub = sinon.stub(thridParty, 'bar')
})
it('should work', () => {
assert.isTrue(bar.calledOnce)
})
}
The stubing does not work. I think it is a timing issue. Bar gets stubed after it has been executed. The example above works if I wrap the first line in a function and execute that function in my test. But that is not what I want. Anybody an idea on how to stub such methods?
In this matter, we can use proxyquire to stub that third party lib as below:
import * as thirdParty from 'third-party-lib';
const proxyquire = require('proxyquire');
const barStub: SinonStub = sinon.stub();
proxyquire('./your-source-file', {
'third-party-lib': { bar: barStub }
});
describe('test', () => {
it('should work', () => {
assert.isTrue(barStub.calledOnce)
})
}
Ref:
https://www.npmjs.com/package/proxyquire
Hope it helps
I think your problem is you are never importing the file where you are doing const foo = bar(). You are just importing bar, thats all! Try importing or requiring your file inside the it block! That should trigger bar() and so, the test should pass!
it('should work', () => {
const foo = require(‘your_foo_file’)
assert.isTrue(bar.calledOnce)
})
Bye!

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;
});

Mock middleware calls in Typescript testing

I am trying to write tests for my Typescript Node.js application. I am using Mocha framework and Chai assertion library. Everything was working fine until custom middlewares(like authentication checking) were added. I tried using Sinon.JS to mock calls to this middleware, but I am having troubles to get it working. Any help would be appreciated.
My app.ts file looks as following:
class App {
public express: express.Application;
constructor() {
this.express = express();
this.routeConfig();
}
private routeConfig(): void {
CustomRouteConfig.init(this.express);
}
}
CustomRouteConfig file:
export default class CustomRouteConfig {
static init(app: express.Application) {
app.use('/:something', SomethingMiddleware);
app.use('/:something', SomeAuthenticationMiddleware);
app.use('/:something/route1/endpointOne', ControllerToTest);
app.use(NotFoundHandler);
app.use(ErrorHandler);
}
}
My ControllerToTest.ts file looks as following:
export class ControllerToTest {
router : Router;
constructor() {
this.router = Router();
this.registerRoutes();
}
public getData(req: Request, res: Response, next: NextFunction) {
//some logic to call Restful API and return data
}
private registerRoutes() {
this.router.get('/', this.getData.bind(this));
}
}
export default new ControllerToTest().router;
My SomethingMiddleware looks as following:
export class SomethingMiddleware {
something = (req: Request, res: Response, next: NextFunction) => {
//some logic to check if request is valid, and call next function for either valid or error situation
}
}
export default new SomethingMiddleware().something;
My test file for this scenario looks as following:
describe('baseRoute', () => {
it('should be json', () => {
return chai.request(app).get('/something/route1/endPointOne')
.then(res => {
expect(res.type).to.eql('application/json');
});
});
});
What is the best way to use Sinon.JS mocks or stubs in this situation? Also, if you think there is a better approach to write tests for this scenario, that would be appreciated as well.
I ended up using sinon stubs. I combined these with Mocha's before and after functions to control what happens when during testing some of the external libraries are called. Also, I have used this on database calls and middleware checks, as these are not of interest to my unit testing. Below is an example of how I did it.
describe("ControllerToTest", () => {
// Sinon stubs for mocking API calls
before(function () {
let databaseCallStub= sinon.stub(DatabaseClass, "methodOfInterest", () => {
return "some dummy content for db call";
});
let customMiddlewareStub= sinon.stub(MyCustomMiddleware, "customMiddlewareCheckMethod", () => {
return true;
});
let someExternalLibraryCall= sinon.stub(ExternalLibrary, "methodInExternalLibrary", () => {
return "some dummy data"
});
});
after(function () {
sinon.restore(DatabaseClass.methodOfInterest);
sinon.restore(MyCustomMiddleware.customMiddlewareCheckMethod);
sinon.restore(ExternalLibrary.methodInExternalLibrary);
});
it("should return data", () => {
return chai.request(app).get('/something/route1/endPointOne')
.then(res => {
expect(res.type).to.eql('application/json');
});
});

Resources