How to use resetMocks:true with module factory mocks - jestjs

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

Related

Typescript import module.exports sub function

I'm using mocha to test a function and I'm facing an error while running the test file.
The file structure is as follows
server
|-test
| |-customer.test.ts
|-customer.js
This is the customer.js file function
module.exports = (instance) => {
instance.validate = async (ctx) => {
// some code here
}
}
This is the mocha test case file customer.test.ts
const instance = require("../customer")
/* eslint-disable no-undef */
describe('customer', () => {
describe('/POST customers', () => {
it('Create Buy customer', (done) => {
instance.validate({
})
done();
});
})
});
But when I run the file using the command mocha .\customer.test.ts it shows me the following error
TypeError: instance.validate is not a function
How do I make the above function execute?
What you're exporting and what you're doing with the import don't match. The problem is (probably) the export. What you have is this:
module.exports = (instance) => {
instance.validate = async (ctx) => {
// some code here
}
}
That exports a function that, when called, will add a validate method to the object that you pass to it. It does not export an object with a validate method, that would look like this:
module.exports = {
validate: async (ctx) => {
// some code here
},
};
So you need to either fix the export (which I suspect is the problem), or (if the export is really meant to do that), test what it actually does by passing in an object and then checking that, after the call, the object has a validate method.

How to change jest.mock value for different tests when mocked class is a dependency on on the SUT?

I am testing a class DogService.ts. But DogService has a private dependency called CatService that gets new'ed up in the constructor. I can successfully mock the dependency using:
jest.mock('../CatService', () => {
return {
CatService: jest.fn().mockImplementation(() => {
return {
someFunction: (async (someArg) => {
return await someAsyncMethod();
})
}
})
}
});
And it gets injected fine in the test:
describe("", () => {
it("", async () => {
const dogService = new DogService();
...
});
});
There's no import for the CatService in my jest test file. The mock I guess is the import. However, I want to swap out the implementation of CatService in different tests. I have tried jest.resetModules() and a bunch of other things. Further, the above only seems to work when the mock is outside any tests. If I move it into the describe() function the real CatService is used.
Any ideas how to do this? Most tutorials are for mocking classes or functions you directly use in the jest test, but obviously I am mocking a dependency.

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!

Resources