简体   繁体   English

如何模拟 AWS sqs 调用进行单元测试

[英]How to mock AWS sqs call for unit testing

I'm using AWS SQS queue in a Node application and I have to write the unit test cases for the same.我在 Node 应用程序中使用 AWS SQS 队列,我必须为此编写单元测试用例。 For that, I want to mock the SQS function call sendMessage() in the test files so, what should I do?为此,我想模拟测试文件中的 SQS 函数调用sendMessage()那么,我该怎么办?

I have tried using aws-sdk-mock but while making the call to the sendMessage() , the function is trying to connect to the Queue URL.我曾尝试使用aws-sdk-mock但在调用sendMessage() ,该函数试图连接到队列 URL。

Test File测试文件

import AWSMock from 'aws-sdk-mock'
import sendMessage from '../api/sqs/producer'

describe.only('Test case for SQS SendMessage', () => {
  it('should return the UserEvent', async () => {
    AWSMock.mock('SQS', 'sendMessage', () => Promise.resolve('Success'))
    const res = await sendMessage('testURL', 'data')
    console.log('RES', res.response.data)
  })
})

Producer File生产者文件

const AWS = require('aws-sdk')

const sqs = new AWS.SQS({
  region: 'us-east-1'
})

const sendMessage = async (msg, queueUrl) => {
  try {
    const params = {
      MessageBody: JSON.stringify(msg),
      QueueUrl: queueUrl
    }
    const res = await sqs.sendMessage(params).promise()
    return res
  } catch (err) {
    console.log('Error:', `failed to send message ${err}`)
    throw new Error(err)
  }
}

export { sendMessage as default }

In above code, I'm expecting the Success as a return value in res在上面的代码中,我期望Success作为res 中的返回值

Output输出

 FAIL  tests/sendMessage.test.js
  ● Console

    console.log api/sqs/producer/index.js:16
      Error: failed to send message UnknownEndpoint: Inaccessible host: `testurl'. This service may not b
e available in the `us-east-1' region.

  ● Test case for SQS SendMessage › should return the UserEvent

    UnknownEndpoint: Inaccessible host: `testurl'. This service may not be available in the `us-east-1' r
egion.

Here is the solution, you don't need aws-sdk-mock module, you can mock aws-sdk by yourself.这是解决方案,您不需要aws-sdk-mock模块,您可以自己模拟aws-sdk

index.ts : index.ts

import AWS from 'aws-sdk';

const sqs = new AWS.SQS({
  region: 'us-east-1'
});

const sendMessage = async (msg, queueUrl) => {
  try {
    const params = {
      MessageBody: JSON.stringify(msg),
      QueueUrl: queueUrl
    };
    const res = await sqs.sendMessage(params).promise();
    return res;
  } catch (err) {
    console.log('Error:', `failed to send message ${err}`);
    throw new Error(err);
  }
};

export { sendMessage as default };

index.spec.ts : index.spec.ts

import sendMessage from './';
import AWS from 'aws-sdk';

jest.mock('aws-sdk', () => {
  const SQSMocked = {
    sendMessage: jest.fn().mockReturnThis(),
    promise: jest.fn()
  };
  return {
    SQS: jest.fn(() => SQSMocked)
  };
});

const sqs = new AWS.SQS({
  region: 'us-east-1'
});

describe.only('Test case for SQS SendMessage', () => {
  beforeEach(() => {
    (sqs.sendMessage().promise as jest.MockedFunction<any>).mockReset();
  });
  it('should return the UserEvent', async () => {
    expect(jest.isMockFunction(sqs.sendMessage)).toBeTruthy();
    expect(jest.isMockFunction(sqs.sendMessage().promise)).toBeTruthy();
    (sqs.sendMessage().promise as jest.MockedFunction<any>).mockResolvedValueOnce('mocked data');
    const actualValue = await sendMessage('testURL', 'data');
    expect(actualValue).toEqual('mocked data');
    expect(sqs.sendMessage).toBeCalledWith({ MessageBody: '"testURL"', QueueUrl: 'data' });
    expect(sqs.sendMessage().promise).toBeCalledTimes(1);
  });

  it('should throw an error when send message error', async () => {
    const sendMessageErrorMessage = 'network error';
    (sqs.sendMessage().promise as jest.MockedFunction<any>).mockRejectedValueOnce(sendMessageErrorMessage);
    await expect(sendMessage('testURL', 'data')).rejects.toThrowError(new Error(sendMessageErrorMessage));
    expect(sqs.sendMessage).toBeCalledWith({ MessageBody: '"testURL"', QueueUrl: 'data' });
    expect(sqs.sendMessage().promise).toBeCalledTimes(1);
  });
});

Unit test result with 100% coverage: 100% 覆盖率的单元测试结果:

 PASS  src/stackoverflow/57585620/index.spec.ts
  Test case for SQS SendMessage
    ✓ should return the UserEvent (7ms)
    ✓ should throw an error when send message error (6ms)

  console.log src/stackoverflow/57585620/index.ts:3137
    Error: failed to send message network error

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.ts |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        3.453s, estimated 6s

Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57585620这是完整的演示: https : //github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57585620

If you have a static sqs test message (for example in a unittest situation where you do hit sqs for some unavoidable reason), you could calculate the md5 sum by simply running the sendMessage against an actual SQS queue (make one quickly in some burner AWS Account, then log the response and md5sum the MessageBody object in the response.如果您有静态 sqs 测试消息(例如,在单元测试情况下,由于某些不可避免的原因您确实命中了 sqs),您可以通过简单地针对实际 SQS 队列运行 sendMessage 来计算 md5 总和(在某些刻录机 AWS 中快速创建一个)帐户,然后记录响应并 md5sum 响应中的 MessageBody 对象。

In your unittest, you can then nock SQS simply by using在你的单元测试中,你可以简单地使用

    const requestId = 'who';
    const messageId = 'wha';
    nock('https://sqs.eu-central-1.amazonaws.com')
        .post('/')
        .reply(
            200,
            `<SendMessageResponse><SendMessageResult><MD5OfMessageBody>193816d2f70f3e15a09037a5fded52f6</MD5OfMessageBody><MessageId>${messageId}</MessageId></SendMessageResult><ResponseMetadata><RequestId>${requestId}</RequestId></ResponseMetadata></SendMessageResponse>`,
        );

Do not forget to change your region and ofcourse the md5sum ;)不要忘记更改您的地区,当然还有 md5sum ;)

This method does not scale obviously, unless you calculate the messageBody's md5sum up front :)此方法不能明显扩展,除非您预先计算 messageBody 的 md5sum :)

Maybe it can help some folks with static unittest messages towards a quick fix.也许它可以帮助一些使用静态单元测试消息的人快速修复。

You can take a look at LocalStack .你可以看看LocalStack It's a lib that runs on docker and can simulate many AWS services running locally, including SQS.它是一个在 docker 上运行的库,可以模拟许多在本地运行的 AWS 服务,包括 SQS。

The problem here is that the SQS service is initialized outside of the handler, ergo at the time the module is requested.这里的问题是 SQS 服务是在处理程序之外初始化的,因此在请求模块时。 As a result, the mock call will happen too late, as the service to be mocked (SQS in this case) was already created.因此,模拟调用将发生得太晚,因为要模拟的服务(在本例中为 SQS)已经创建。

From the docs :文档

NB: The AWS Service needs to be initialised inside the function being tested in order for the SDK method to be mocked注意:AWS 服务需要在被测试的函数内初始化,以便模拟 SDK 方法

Updating your producer file as follows will correctly work with aws-sdk-mock :如下更新您的生产者文件将正确地与aws-sdk-mock

const AWS = require('aws-sdk')

let sqs;

const sendMessage = async (msg, queueUrl) => {
  if(!sqs) {
    sqs = new AWS.SQS({
      region: 'us-east-1'
    });
  }
  try {
    const params = {
      MessageBody: JSON.stringify(msg),
      QueueUrl: queueUrl
    }
    const res = await sqs.sendMessage(params).promise()
    return res
  } catch (err) {
    console.log('Error:', `failed to send message ${err}`)
    throw new Error(err)
  }
}

export { sendMessage as default }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM