How to mock external class in another initialized in another class? - javascript

I have following files and classes
//A.ts
export abstract class A{
protected abstract method1(arg){
}
}
// Session.ts
export class Session{
constructor(socket: WebSocket){
this._socket = socket;
this._socket.on('close', () => {do something} );
this._socket.on('error', () => {do something} );
this._socket.on('message', () => {do something} );
}
setSomeSocket(someSocket: WebSocket){
this._anotherSocket = someSocket;
this._anotherSocket.on('close', () => {do something} );
this._anotherSocket.on('error', () => {do something} );
};
}
// B.ts
export class B extends A{
protected async method1(arg){
try{
let tempSession = new Session();
tempSession.setSomeSocket(socket);
// do something with temp
}
catch(exception){
}
}
}
// B.spec.ts
describe("B test", () => {
it("some test", () => {
const varB = new B();
await varB["method1"](arg);
expect(spied).toBeCalledTimes(1);
});
})
I want to mock the class Session so that it doesn't create any problem in socket.on() methods. I am sending mocked websockets.
The class is external and I want the mock implementation inside test file such that when running the test, the mocked class gets called inside B.ts

You can mock directly with the jest:
jest.mock('./Session');
beforeEach(() => {
Session.mockClear();
})
Check out the manual mocking and auto mocking from Jest documentation.

As stated in another comment you can mock Session with jest
const mockSetSameSocks = jest.fn(); // note name starts with mock, important
jest.mock('./Session', () => {
return {
Session: jest.fn(() => ({
setSameSocks: mockSetSameSocks,
}))
}
});
// B.spec.ts
describe("B test", () => {
beforeEach(() => {
mockSetSameSocks.mockClear();
})
it("some test", () => {
const varB = new B();
await varB["method1"](arg);
expect(mockSetSameSocks).toBeCalledTimes(1);
});
})

Related

mocking private methods to test an exported method fails in jest

I'm having an actual class like this
my-file.js
const methodA = () => { return 'output-from-methodA'; }
const methodB = () => { const b = methodA(); b.c = "out-put bind"; return b; }
module.exports = {
methodB
}
my-file.test.js
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
methodB();
expect(methodA).toHaveBeenCalled()
}
});
here methodA is private method, so it is not explicit to the test file, then how i ensure it is called or not in the test files
There is no way to test the private function, only the alternative way found is to test the outputs like
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
const result = methodB();
expect(result.b.c).toBe("out-put bind")
}
});

How to mock child function with Jest

I have a sendMail.js class content as below
export default function registerEmailHandlers() {
const sendMailAsync = async (emailAddress) => {
return await sendMail();
}
return {
sendMailAsync
}
}
How can I write Jest mock for this type of code?
I want mock sendMailAsync always return 'some test'.
I tried to research but nothing same that what I want.
Could you do something like
const mockSendMailAsync = jest.fn());
jest.mock('./path/to/file/sendMail', () => (
sendMailAsync = mockSendMailAsync
)
Then you could assert that mockSendMailAsync has been called

How to jest spyOn a commonJS default export

Is it possible to jest.spyOn a default export so that I can call the mockImplementation method to change what the function does before each test?
// code.js
module.exports = () => {
// some irrelevant code I want to rewrite with a mock
};
// test
const code = require('./code.js');
const mockCode = jest.spyOn(code, 'default'); // this line doesn't work with the error: "Cannot spy the default property because it is not a function; undefined given instead"
it('some test', async () => {
mockCode.mockImplementationOnce(() => { console.log('test') });
});
I've also tried to use jest.mock() unsuccessfully:
const code = require('./code');
jest.mock('./code',
() => () => {
console.log('test');
}
);
it('some test', async () => {
code.mockImplementationOnce(() => { console.log('test2') }); // error
});

Unit testing chained promises

How would I unit test a class method that calls an imported class's method that is a promise? I have the following structure:
import { SomeClass } from 'some-library';
class MyClass extends AnotherClass {
myMethod() {
const someClass = new SomeClass();
return someClass.somePromiseMethod('someParam')
.then(response => response.data)
.then(response => {
// Do stuff
});
}
}
I have the following test
describe('myMethod', () => {
it('does something', async () => {
const inst = new MyClass();
const stub = sinon.stub(SomeClass, 'somePromiseMethod')
.resolves(Promise.resolve({
data: [],
}));
await inst.myMethod();
expect(stub.callCount).to.equal(1);
});
});
Which is still pretty bare as I'm not sure how to approach this. Would it be better to break down the code in the thens?
UPDATE
Apparently SomeClass is a singleton and sinon was throwing an error saying somePromiseMethod is a non-existent own property. I changed the stub to call on its prototype instead and now the stub is being called.
class MyClass extends AnotherClass {
myMethod() {
const someClassInstance = SomeClass.getInstance();
return someClassInstance.somePromiseMethod('someParam')
.then(response => response.data)
.then(response => {
// Do stuff
});
}
}
describe('myMethod', () => {
it('does something', async () => {
const inst = new MyClass();
const stub = sinon.stub(SomeClass.prototype, 'somePromiseMethod')
.resolves(Promise.resolve({
data: [],
}));
await inst.myMethod();
expect(stub.callCount).to.equal(1);
});
});
Now, since then second then would just return data, I could just put //Do stuff in a separate function and test that.
You are stubbing out the wrong method somePromiseMethod exists on the prototype of SomeClass so you need to stub that instead. Sinon should let you do something like:
const stub = sinon.stub(SomeClass.prototype, 'somePromiseMethod')
// You may be able to remove the Promise.resolve as well, as I think resolves does this for you
.resolves({
data: [],
});

How to undo mocked require in jest?

I am mocking a library by doing this:
let helperFn;
let mock;
beforeEach(() => {
mock = jest.fn();
require('./helperFn').default = mock;
})
If I do this in a test, does it mean that from now on within the whole test suite that default function of helperFn will be associated with that mock?
In the Jest documentations I see how to reset the mock, but I don't see how to remove the mock from a required function. I am concerned that from that test on, all the calls into helperFn.default will see that mock.
ES6 modules
Here is an ES6 example:
helperFn.js
export default () => 'original';
code.js
import helperFn from './helperFn';
export const func = () => helperFn();
code.test.js
import * as helperFnModule from './helperFn';
import { func } from './code';
describe('helperFn mocked', () => {
let mock;
beforeEach(() => {
mock = jest.spyOn(helperFnModule, 'default');
mock.mockReturnValue('mocked');
});
afterEach(() => {
mock.mockRestore();
});
test('func', () => {
expect(func()).toBe('mocked'); // Success!
});
});
describe('helperFn not mocked', () => {
test('func', () => {
expect(func()).toBe('original'); // Success!
});
});
Details
Since ES6 imports are live views of the module exports, it is easy to mock an export and then restore it afterwards.
Node.js modules
Here is a Node.js example:
helperFn.js
exports.default = () => 'original';
code.js
const helperFn = require('./helperFn').default;
exports.func = () => helperFn();
code.test.js
describe('helperFn mocked', () => {
beforeEach(() => {
const helperFnModule = require('./helperFn');
helperFnModule.default = jest.fn(() => 'mocked');
});
afterEach(() => {
jest.resetModules();
});
test('func', () => {
const { func } = require('./code');
expect(func()).toBe('mocked'); // Success!
});
});
describe('helperFn not mocked', () => {
test('func', () => {
const { func } = require('./code');
expect(func()).toBe('original'); // Success!
});
});
Details
The default export gets remembered by code.js when it runs, so changing the default export of helperFn.js doesn't affect func once code.js is required. Jest also caches modules and returns the same module for multiple require calls unless jest.resetModules is called.
So for Node.js modules it is often easiest to require code within the test itself and use jest.resetModules to reset any mocking.

Categories