Mocking AWS Services — the easy way

Mayank Nayyar
6 min readJul 11, 2020

--

When it comes to writing unit tests, there is no surprise that developers hate but we all understand the purpose it serves.

Assuming that we all have been writing a good number of test cases for our code, the mandatory part of highlighting the importance of unit testing is skipped.

In this article, we will be utilizing the Jest framework to its full capacity for writing our unit test cases. Jest is a very popular javascript-based testing framework available out there and works extremely well with projects using Babel, TypeScript, Node, React, Angular, Vue, and more!

We shall next see the most basic code snippet just to jog our memory about the coding pattern in Jest.

//index.tsexport const sampleMockFunction = (message: string): string => {
switch (message) {
case "When the snows fall and the white winds blow";
return "the lone wolf dies, but the pack survives!"
default:
return "The North Remembers!"
}
}
// index.test.tstest("sample mock func positive scenario", () => {
const message = "When the snows fall and the white winds blow";
const expResult = "the lone wolf dies, but the pack survives!";
expect(index.sampleFunction(message)).toBe(expResult);
});
test("sample mock func negative scenario", () => {
const message = "Game of thrones!"
const expResult = "the lone wolf dies, but the pack survives!";
expect(index.sampleFunction(message))).toBe(expResult);
});

Now that we have revised our basics, we are all set to start the next section which is mocking AWS resources while writing unit test cases.

As per the latest findings, Amazon owns almost half of the world’s public-cloud infrastructure market and thus it matters more than ever now that we understand the impact of this on the way we design and code our systems.

Since writing unit tests is one of the fundamental pillars of resilient systems, we need to start including AWS resources in our test cases. The one way to do is use AWS resources directly in our unit testing — which involves the cost of execution and the, increase in time along with the challenge to test our code locally. The other way to accomplish this is by mocking the AWS services themselves so that we eliminate all the need to set up and maintain infra for our test environment.

Throughout the next course of the article, we will be mocking the highly used services of AWS services like S3, Dynamo Db, and SQS. The other services can be mocked in a similar pattern.

Mocking Simple Storage Service (S3)

S3 is one of the oldest services provided by AWS and to date and is one of the widely used services available.

Mocking Object Retrieval Action

We can retrieve any object stored on S3 using the GetObject action on the bucket. The following code snippet depicts how we can mock GetObject S3 action.

//s3.tsexport const mockS3GetObject = async() : Promise <any> => {
const params = {
Bucket : "test-bucket",
Key : "key",
};

const s3 = new AWS.S3({apiVersion: '2006-03-01'});
const s3Result = await s3.getObject(params);
return s3Result.Body;
}
//s3.test.tsconst mockS3GetObject = jest.fn();jest.mock("aws-sdk", () => {
return {
S3: jest.fn(() => ({
getObject : mockS3GetObject
}));
};
});
test ("mock s3 get object call", async () => { const expectedResult = "file buffer";
mockS3GetObject.mockImplementation (() => {
return {
Body: "file buffer"; //for demonstration string is returned
};
});
expect (await s3.mockS3GetObject()).toEqual(expectedResult);
});

Mocking Object Storage Action

Similar to retrieving objects, we can store any object using the PutObject action on any specified bucket. The following code snippet depicts how we can mock the PutObject action.

// s3.tsexport const mockS3PutObject = async() : Promise <any> => {
const params = {
Bucket : "test-bucket",
Key : "key",
Body : "1,2,3"
};

try{
const s3 = new AWS.S3({apiVersion: '2006-03-01'});
await s3.putObject(params).promise();
return "success";

} catch (err){
return "failure";
}
}
// s3.test.tsconst mockS3PutObject = jest.fn();jest.mock("aws-sdk", () => {
return {
S3: jest.fn(() => ({
putObject: mockS3PutObject
}));
};
});
test ("mock s3 put object call", async () => {
const expectedResult = "success";
mockS3PutObject.mockImplementation(() => {
return {
statusCode: 200
};
});
expect(await s3.mockS3PutObject()).toEqual(expectedResult);
});

Mocking Dynamo Db

Dynamo Db is one of the most popular AWS services that offers fully managed No-SQL solutions.

Mocking Item Retrieval Action

We can retrieve an item from the Dynamo using the get method of the Dynamodb document client. The following code snippet depicts how we can mock the DocumentClient.get method.

// dynamo.tsexport const mockDynamoGetItem = async () : Promise <any> => {
const idx = 1;
const params = {
TableName : TABLE_NAME,
Key: {
HashKey: idx,
}
}
const client = new DocumentClient({region: 'us-east-1'}); try{ const result = await client.get(params).promise();
return result.Item;
}catch (err){
return { "err" : "Error fetching data from Dynamo" };
}
}
// dynamo.test.tsconst mockDynamoGetItem = jest.fn();jest.mock('aws-sdk/clients/dynamodb', () => { return {
DocumentClient: jest.fn(() => ({
get: mockDynamoGetItem
}))
};
});
test ("mock dynamo get item", async () => { const expectedResult = {"idx" : 1, "status" : "in progress"}; mockDynamoGetItem.mockImplementation (() => {
return {
promise() {
return Promise.resolve({});
}
}
});
expect(await dynamo.mockDynamoGetItem()).toEqual(expectedResult);
});

Mocking Item Storage Action

We can store an item into the DynamoDb using the put method of DynamoDb document client. The following code snippet depicts how we can mock the DocumentClient.get method.

// dynamo.tsexport const mockDynamoPutItem = async () : Promise <any> => {  const item : object = {
"idx" : 2,
"status" : "success"
};
const params = {
TableName : TABLE_NAME,
Item: item
}

const client = new DocumentClient({region: 'us-east-1'});

try{
return await client.put(params).promise(); }catch (err){
return {"err" : err};
}
}
// dynamo.test.tsconst mockDynamoPutItem = jest.fn();jest.mock('aws-sdk/clients/dynamodb', () => { return {
DocumentClient: jest.fn(() => ({
put: mockDynamoPutItem
}))
};
});
test ("mock dynamo put item", async () => { const expectedResult = {"idx" : 1, "status" : "in progress"}; mockDynamoPutItem.mockImplementation (() => {
return {
promise() {
return Promise.resolve({});
}
}
});
expect(await dynamo.mockDynamoPutItem()).toEqual(expectedResult);
});

Mocking Simple Queue Service (SQS)

SQS is the fully managed queuing service offered by AWS. It allows sending, storing, and retrieving messages among different software components.

Mocking Send Message Action

We can push a message using the SendMessage action on an SQS queue. The following snippet depicts how we can mock the SendMessage action.

//sqs.tsexport const mockSQSendMessage = async () : Promise <any> => {
const sqs = new AWS.SQS();
const region: string = 'ap-south-east-1';
const accountId: string = '242******';
const queueName: string = 'sampleQueue';
const queueUrl: string = `https://sqs.${region}.amazonaws.com/${accountId}/${queueName}`;
const messageBody : string = "Hello from the other side!"
let result: string;

try {
await sqs.sendMessage({
QueueUrl: queueUrl,
MessageBody: messageBody,
MessageAttributes: {
AttributeNameHere: {
StringValue: 'String Value',
DataType: 'String',
},
},
});
result = "Message successfully pushed to queue!";

} catch (error) {
result = 'Error pushing message to the queue';
}
return result;
}
//sqs.test.tsconst mockSQSendMessage = jest.fn();jest.mock("aws-sdk", () => {
return {
SQS: jest.fn(() => ({
sendMessage : mockSQSendMessage
}));
};
});
test ("mock sqs send message call", async () =>{ const expectedResult = "Message successfully pushed to queue!";

mockSQSendMessage.mockImplementation (() => {
return {
"MD5OfMessageBody": "51b0a325...39163aa0",
"MD5OfMessageAttributes": "00484c68...59e48f06",
"MessageId": "da68f62c-0c07-4bee-bf5f-7e856EXAMPLE"
};
});

expect(await sqs.mockSQSendMessage()).toEqual(expectedResult);
});

Conclusion

With this, we come to the end of this article. Just as highlighted throughout, we can mock any AWS service following the pattern and the steps described in this article for a few widely used services.

If this article brings even the slightest change in the way we write our test cases, then its safe to assume that it has served its purpose. Keep building stuff and keep writing test cases!

--

--