I have a function that returns a promise and calls another function which takes a call back as a parameter but I'm unable to to figure how to mock the callback function nor the function calling it.
The function I'm trying to mock is "listenOnAMessageWithCallBack" from a service I'm injecting and I also want to mock the callback function that it calls.
My implementation:
async getUsername(): Promise<string> {
return await new Promise<string>((resolve, reject) => {
try {
this.electronController.sendMessage('userName');
let cb = (event, username) =>{
this.username = username;
let user = this.stripDomain(this.username);
resolve(user);
};
this.electronController.listenOnAMessageWithCallBack('catchUser', cb);
} catch (err) {
reject(err);
}
})
}
and my test is the following:
it('testing function with callback', async() => {
const stripDomain = spyOn(service, 'stripDomain').and.callFake(()=>{
service.username = service.username.split('\\').reverse()[0];
return service.username;
});
let cb = (event, username)=>{Promise.resolve('username')}
spyOn(electronController, 'listenOnAMessageWithCallBack').withArgs('message').and.callFake(()=>{});
let username = await service.getUsername();
expect(username).toBe('username');
expect(stripDomain).toHaveBeenCalledTimes(1);
});
I'm getting the following error when I run the test: Spy 'listenOnAMessageWithCallBack' received a call with arguments [ 'catchUser', Function ] but all configured strategies specify other arguments.
How can I mock the callback function and its calling function ?
Thanks in advance.
I have a function that returns a promise and calls another function which takes a call back as a parameter but I'm unable to to figure how to mock the callback function nor the function calling it.
The function I'm trying to mock is "listenOnAMessageWithCallBack" from a service I'm injecting and I also want to mock the callback function that it calls.
My implementation:
async getUsername(): Promise<string> {
return await new Promise<string>((resolve, reject) => {
try {
this.electronController.sendMessage('userName');
let cb = (event, username) =>{
this.username = username;
let user = this.stripDomain(this.username);
resolve(user);
};
this.electronController.listenOnAMessageWithCallBack('catchUser', cb);
} catch (err) {
reject(err);
}
})
}
and my test is the following:
it('testing function with callback', async() => {
const stripDomain = spyOn(service, 'stripDomain').and.callFake(()=>{
service.username = service.username.split('\\').reverse()[0];
return service.username;
});
let cb = (event, username)=>{Promise.resolve('username')}
spyOn(electronController, 'listenOnAMessageWithCallBack').withArgs('message').and.callFake(()=>{});
let username = await service.getUsername();
expect(username).toBe('username');
expect(stripDomain).toHaveBeenCalledTimes(1);
});
I'm getting the following error when I run the test: Spy 'listenOnAMessageWithCallBack' received a call with arguments [ 'catchUser', Function ] but all configured strategies specify other arguments.
How can I mock the callback function and its calling function ?
Thanks in advance.
Share Improve this question asked Jan 10, 2020 at 17:28 mahmoud chebbanimahmoud chebbani 751 gold badge2 silver badges7 bronze badges 1- i was only looking to stub out a method with some custom implementation and stumbled on this post and the callFake idea used in the question helped. – oomer Commented Jul 14, 2021 at 12:55
1 Answer
Reset to default 6You're getting that error message because you configured your listenOnAMessageWithCallBack
spy with .withArgs('message')
, so your spy will only be used in place of that method if that method is called with argument 'message'
. However, in your service code that method is called with 'catchUser'
and a callback function, not with 'message'
, so your spy is never invoked. If you remove the .withArgs('message')
condition your spy will be invoked regardless of the arguments passed to the actual method.
Once you get that working and your spy is invoked, then within the spy's callFake
function you can get a hold of the callback that's being passed into the method in your service code:
spyOn(electronController, 'listenOnAMessageWithCallBack').and.callFake(
(msg, cb) => {
expect(msg).toBe('catchUser');
expect(typeof cb).toBe('function');
// cb here is the cb being passed into listenOnAMessageWithCallBack in your service
// code, so you need to invoke it here yourself or the promise won't get resolved
cb('mockEvent', 'mockUsername');
}
);
You can't really mock the callback because it's a local variable within your service, but since you can get a hold of it and manually invoke it in your test, you should be able to test its effects.
You'd need to figure out exactly what you expect your code to be doing, though, because that callback sets service.username
to the username passed to it, but it looks like in your test you're trying to spy on service.stripDomain
and have that set service.username
instead. So it looks like you need to determine exactly what you're trying to test here.
Here's a stackblitz showing the callFake
working and giving you access to the callback function.