How to mock sns?

Viewed 2698

I faced to some issue when I wanted to test my handler which publishes a message on SNS.

Here is my code:

// my handler 
export const handler = (event) => {
    try { 
        await emitDeletionComplete(classified.metadata.classifiedRequestId);
     }                         
catch(e) { 
        console.error(e);
         throw new Error(e); 
    }
}

// my SNS service 
import SNS from 'aws-sdk/clients/sns';
const snsClient = new SNS({region: process.env.AWS_REGION});

export const emitDeletionComplete = async (id) => {
  try {
    await snsClient.publish({
      Message: JSON.stringify({
        type: 'DELETE_COMPLETE',
        data: {
          id
        }
      }),
      TopicArn: process.env.SNS_ARN
    }).promise();
  } catch(err) {
    console.error(err, err.stack);
    throw new Error('We do not succeed to publish the message DELETE_COMPLETE to ARN: ' + process.env.SNS_ARN);
  }
};

When i want to test, i try to do :

import { handler } from '../../../src/handler/dispatch-deletion-to-legacy';
import SNS from 'aws-sdk/clients/sns';

jest.mock('aws-sdk/clients/sns', () => {
  return {
    __esModule: true,
    default: jest.fn(() => {
      return {
        publish: jest.fn().mockReturnThis(),
        promise: jest.fn(),
      }
    }),
  };
});

[...]
it('should delete', () => {
    let sns = new SNS();
    const event = {
      Records: [
        {
          body: JSON.stringify({...some event...})
        }
      ]
    }

    handler(event);

    expect(sns.publish().promise).toBeCalledTimes(1);
});

Apparently, it is never called. I don't get why.Maybe my mock is completely wrong.

i'm stuck with it for few hours now...Any idea how can I mock correctly ?

EDIT 1 : https://github.com/JLGouwy/aws-sns-mock-test

thanks

1 Answers

In short, your instance - let sns = new SNS(); is not instance used by your production code.

In long, you have keeping track of usage of sns mock instance, jest document

This is your example, but I changed a little. I will try to explain by comments in bellow example.

Production code:

import SNS from 'aws-sdk/clients/sns';
const snsClient = new SNS({ region: process.env.AWS_REGION });

exports.handler = async (event) => {
  try {
    const { classifiedRequestId } = JSON.parse(event.Records[0].body); // try to get classifiedRequestId from Record body
    await emitDeletionComplete(classifiedRequestId);
  } catch (e) {
    console.error(e);
    throw new Error(e);
  }
}

const emitDeletionComplete = async (id) => { // in the same file with handler function
  try {
    await snsClient.publish({
      Message: JSON.stringify({
        type: 'DELETE_COMPLETE',
        data: {
          id
        }
      }),
      TopicArn: process.env.SNS_ARN
    }).promise();
  } catch (err) {
    console.error(err, err.stack);
    throw new Error('We do not succeed to publish the message DELETE_COMPLETE to ARN: ' + process.env.SNS_ARN);
  }
};

Spec file

import SNS from 'aws-sdk/clients/sns';
import { handler } from "."; // handler function in the same directory

const mockPromise = jest.fn(); // mock for deep function - promise
jest.mock('aws-sdk/clients/sns', () => {
  // return a function as a constructor
  return jest.fn().mockImplementation(function () { // "normal function" not arrow function
    this.publish = jest.fn(() => ({ // mock publish function
      promise: mockPromise, // returns an object what includes promise property
    }));
  });
});

describe("handler function", () => {
  it("should publish delete command", async () => {
    const classifiedRequestId = "12345";
    const event = {
      Records: [
        {
          body: JSON.stringify({ // input data
            classifiedRequestId,
          }),
        }
      ]
    };

    await handler(event);

    // get instance of SNS, an instance has been created in production code
    const snsMocked = SNS.mock.instances[0];
    
    // expect publish function will be call with expected parameter
    expect(snsMocked.publish).toHaveBeenCalledWith(
      {
        Message: JSON.stringify({
          type: 'DELETE_COMPLETE',
          data: {
            id: classifiedRequestId,
          }
        }),
        TopicArn: process.env.SNS_ARN
      }
    );

    // expect promise function will be call too
    expect(mockPromise).toHaveBeenCalled();
  })
});
Related