Reputation: 530
I'm using twilio package in my backend which uses loopback4 with Typescript. I'm writing unit test cases using Mocha,Jest and sinon but facing following difficulties.
I'm having a twilio service which looks like this
export class TwilioService {
twilioBaseUrl = 'https://video.twilio.com';
twilioClient: TwilioClient;
constructor(
@inject(TwilioBindings.config)
private readonly twilioConfig: TwilioConfig,
@repository(VideoChatSessionRepository)
private readonly videoChatSessionRepository: VideoChatSessionRepository,
@repository(SessionAttendeesRepository)
private readonly sessionAttendeesRepository: SessionAttendeesRepository,
) {
const {accountSid, apiSid, apiSecret, authToken} = twilioConfig;
if (!(accountSid && apiSid && apiSecret && authToken)) {
throw new HttpErrors.BadRequest(`Twilio API credentials are not set`);
}
this.twilioClient = twilio(twilioConfig.accountSid, twilioConfig.authToken);
}
async deleteArchive(archiveId: string): Promise<void> {
const result = await this.twilioClient.video
.recordings(archiveId)
.remove((err, items) => {
if (err) {
throw new HttpErrors.ExpectationFailed(`Error deleting archive`);
}
});
if (!result) {
throw new HttpErrors.ExpectationFailed(`Error deleting archives`);
}
return Promise.resolve();
}
}
Now here for the method deleteArchive(arciveId:string):Promise<void>
I write the test cases as
describe('deleteArchive', () => {
it('delete the archive with given arhive id', async () => {
const recordingContext = sinon.createStubInstance(RecordingContext);
recordingContext.remove.resolves(true);
sinon
.stub(twilioService.twilioClient.video, 'recordings')
.returns(recordingContext);
const result = await twilioService.twilioClient.video
.recordings(archiveId)
.remove();
sinon.assert.calledOnce(recordingContext.remove);
});
});
it gives me error that
Error: The requested resource /Recordings/RTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX was not
found
at success (node_modules/twilio/lib/base/Version.js:113:15)
at Promise_then_fulfilled (node_modules/q/q.js:766:44)
at Promise_done_fulfilled (node_modules/q/q.js:835:31)
at Fulfilled_dispatch [as dispatch] (node_modules/q/q.js:1229:9)
at Pending_become_eachMessage_task (node_modules/q/q.js:1369:30)
at RawTask.call (node_modules/asap/asap.js:40:19)
at flush (node_modules/asap/raw.js:50:29)
at processTicksAndRejections (node:internal/process/task_queues:78:11)
The actual issue that seems to me is that actual recordings
method is called instead of my stubbed one, which throws the error.
So my question is
twilioClient.video.recordings
returns an object which has a method remove
how to deal with such scenario where we want to stub a method of an object which is also the result of stubbed method.
Upvotes: 0
Views: 235
Reputation: 530
The problem that I figured out is my stubbed function was not called. Instead it is the actual one which throws the error.
So I mocked the entire twilio library using proxyquire as follows:
describe('deleteArchive', () => {
it('delete the archive with given arhive id', async () => {
const removeStub = sinon.stub().resolves(true);
const recordingStub = sinon.stub().returns({
remove: removeStub,
});
const TwilioMockService = proxyquire(
'../../../providers/twilio/twilio.service',
{
twilio: (accountSid: string, authToken: string) => {
return {
video: {
recordings: recordingStub,
},
};
},
},
).TwilioService;
const twilioMockServiceInstance = new TwilioMockService(
twilioConfig,
videoChatSessionRepo,
sessionAttendeesRepo,
);
twilioProvider = new TwilioProvider(twilioMockServiceInstance);
await twilioProvider.value().deleteArchive(archiveId);
sinon.assert.calledOnce(removeStub);
});
});
PS: twilio provider is just a wrapper over TwilioService. It just exposes the methods of twilioServie and nothing else.
It is required because of the lb4 architecture and nothing else
Upvotes: 1