Reputation: 1301
I'm using AWS SQS queue in a Node application and I have to write the unit test cases for the same. For that, I want to mock the SQS function call sendMessage()
in the test files so, what should I do?
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.
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
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.
Upvotes: 20
Views: 42803
Reputation: 19738
The problem here is that the SQS service is initialized outside of the handler, ergo at the time the module is requested. As a result, the mock call will happen too late, as the service to be mocked (SQS in this case) was already created.
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
Updating your producer file as follows will correctly work with 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 }
Upvotes: 2
Reputation: 79
You can take a look at LocalStack. It's a lib that runs on docker and can simulate many AWS services running locally, including SQS.
Upvotes: 0
Reputation: 29
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.
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 ;)
This method does not scale obviously, unless you calculate the messageBody's md5sum up front :)
Maybe it can help some folks with static unittest messages towards a quick fix.
Upvotes: 1
Reputation: 102587
Here is the solution, you don't need aws-sdk-mock
module, you can mock aws-sdk
by yourself.
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
:
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:
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
Upvotes: 37