0

I have implemented Sendgird SDK to send emails and need to stub it in my tests. I am getting following, error

Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

It seems this issue is not related to test execution time but some related to Promise.

This is the original class,

import sendGridMail from '@sendgrid/mail';
import Promise from 'bluebird';

export default class sendGridClient {
  constructor() {
    this.apiKey = 'Test key';
    this.sendGrid = Promise.promisifyAll(sendGridMail);
    this.sendGrid.setApiKey(this.apiKey); 
 }

 async send(to, from, subject, description, body) {
   const message = {
     to,
     from,
     subject,
     text: description,
     html: body,
   };

   const response = await this.sendGrid.sendAsync(message);

   return response;
 }
}

Test code,

import { expect } from 'chai';
import Promise from 'bluebird';
import sinon from 'sinon';
import sendGridMail from '@sendgrid/mail';

import sendGridClient from './../../../src/lib/sendGrid/sendGridClient';

 describe.only('sendGridClient', function () {
   it('sends email successfully', async function () {
     const sendGrid = Promise.promisifyAll(sendGridMail);

     const setApiKey = sinon.stub(sendGrid, 'setApiKey');
     const mandrillSend = sinon
      .stub(sendGrid, 'send')
      .returns(Promise.resolve({}));

     const sendGridApi = new sendGridClient();
     const actualResult = await sendGridApi.send(
       'supprt@test.com',
       'user@gmail.com',
       'Subject',
       'Email description',
       'Email body',
     );

     expect(mandrillSend.callCount).to.equal(1);

     setApiKey.restore();

     mandrillSend.restore();
  });
});
Shaolin
  • 2,541
  • 4
  • 30
  • 41
  • Shouldn't the `sinon.stub(sendGrid, 'send')` instead be `sinon.stub(sendGrid, 'sendAsync')` since you performed `Promise.promisifyAll()` on the original object? – Sven Mar 20 '18 at 06:45
  • Possible duplicate of [How to increase timeout for a single test case in mocha](https://stackoverflow.com/questions/15971167/how-to-increase-timeout-for-a-single-test-case-in-mocha) – Troopers Mar 20 '18 at 09:33
  • @Svenskunganka Nop, it says `TypeError: Cannot promisify an API that has normal methods with 'Async'-suffix` – Shaolin Mar 20 '18 at 22:59
  • @Troopers That question is related to timeout but this is not, this seems some issue with The Promise or the way I stub it. – Shaolin Mar 20 '18 at 23:00

1 Answers1

0

First, we should know the below thing about Promise.promisifyAll:

The entire prototype chain of the object is promisified on the object. Only enumerable are considered. If the object already has a promisified version of the method, it will be skipped. The target methods are assumed to conform to node.js callback convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.

There are two solutions:

  1. Replace the sendGridMail.send with a Error-first callbacks.
  2. Stub the promisified method which it's name will be the original method name suffixed with suffix (default is "Async").

E.g.

sendGridClient.test.js:

// @ts-nocheck
import { expect } from 'chai';
import sinon from 'sinon';
import sendGridMail from '@sendgrid/mail';
import sendGridClient from './sendGridClient';

describe('sendGridClient', function () {
  it('should send email successfully (replace send method with stub)', async function () {
    const sendOriginal = sendGridMail.send;
    const setApiKey = sinon.stub(sendGridMail, 'setApiKey').returns('mocked');
    const mandrillSend = sinon.stub().callsFake((data, cb) => {
      cb(null, {});
    });
    sendGridMail.send = mandrillSend;
    const sendGridApi = new sendGridClient();

    const actualResult = await sendGridApi.send(
      'supprt@test.com',
      'user@gmail.com',
      'Subject',
      'Email description',
      'Email body',
    );
    expect(actualResult).to.be.deep.equal({});
    sinon.assert.calledWith(setApiKey, 'Test key');
    expect(mandrillSend.callCount).to.equal(1);

    setApiKey.restore();
    sendGridMail.send = sendOriginal;
  });

  it('sends email successfully (stub sendAsync method)', async function () {
    const setApiKey = sinon.stub(sendGridMail, 'setApiKey').returns('mocked');
    const sendGridApi = new sendGridClient();
    const mandrillSend = sinon.stub(sendGridMail, 'sendAsync').resolves({});

    const actualResult = await sendGridApi.send(
      'supprt@test.com',
      'user@gmail.com',
      'Subject',
      'Email description',
      'Email body',
    );
    expect(actualResult).to.be.deep.equal({});
    sinon.assert.calledWith(setApiKey, 'Test key');
    expect(mandrillSend.callCount).to.equal(1);

    setApiKey.restore();
    mandrillSend.restore();
  });
});

unit test result:

  sendGridClient
    ✓ should send email successfully (replace send method with stub)
    ✓ sends email successfully (stub sendAsync method)


  2 passing (9ms)

-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |     100 |      100 |     100 |     100 |                   
 sendGridClient.ts |     100 |      100 |     100 |     100 |                   
-------------------|---------|----------|---------|---------|-------------------

package version: "bluebird": "^3.7.2"

Lin Du
  • 88,126
  • 95
  • 281
  • 483