如何模拟sns?

How to mock sns?

当我想测试在 SNS 上发布消息的处理程序时,我遇到了一些问题。

这是我的代码:

// 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);
  }
};

当我想测试时,我会尝试做:

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);
});

显然,它从未被调用过。我不明白 why.Maybe 我的模拟完全错误。

我现在坚持了几个小时...知道我怎样才能正确模拟吗?

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

谢谢

简而言之,您的实例 - let sns = new SNS(); 不是您的生产代码使用的实例。

总之,您已经跟踪了 sns 模拟实例的使用情况,jest document

这是你的例子,但我做了一点改动。我将尝试在下面的例子中通过评论来解释。

生产代码:

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);
  }
};

规格文件

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();
  })
});