I am trying to mock the AWS SDK. I have a function, ingest
that does some logic (including some calls to the AWS SDK sqs.deleteMessage
function) and then calls ingestAndSave
(which also makes some calls to the sqs.deleteMessage
function).
- When testing the
ingest
function, I want to mock myingestAndSave
function so I can have those tests focus purely on theingest
function logic. - Then I want to test the
ingestAndSave
function logic in its own describe block - and test the real thing, not the mocked version. - When testing either
ingest
oringestAndSave
functions I need to use my mockedsqs.deleteMessage
My most recent attempt includes following the path here: https://www.lucasamos.dev/articles/jestmocking. My test suite looks like this:
const ingest = require('./ingest')
const TEST_CONSTANTS = require('../../../tests/constants')
const mockdeleteMessage = jest.fn().mockImplementation( ()=> {
console.log("MOCK DELETE MESSAGE FUNCTION");
return {}
} )
jest.mock('aws-sdk', () => {
return {
config: {
update() {
return {}
}
},
SQS: jest.fn(() => {
return {
deleteMessage: jest.fn(( { params } )=> {
return {
promise: () => mockdeleteMessage()
}
}),
}
})
}
});
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
test("ingestAndSave will be called two times when event has two Records", async () => {
await ingest.ingest(TEST_CONSTANTS.sqsIngestEventMessageWithTwoRecords)
expect(ingestAndSaveFunctionSpy).toHaveBeenCalledTimes(2)
})
afterEach(() => {
jest.resetAllMocks()
})
afterAll(() => {
jest.restoreAllMocks()
})
})
describe("FUNCTION: ingestAndSave", () => {
let validParams = {camera: TEST_CONSTANTS.validCameraObject, sourceQueueURL:"httpexamplecom", receiptHandle: "1234abcd"}
test("Will call deleteMessage once when given valid paramters", async () => {
await ingest.ingestAndSave(validParams)
expect(mockdeleteMessage.mock.calls.length).toBe(1)
})
/** snip other tests */
})
Both of the above functions run through code that looks just like this:
let deleteResp;
try {
deleteResp = await sqs.deleteMessage({ QueueUrl: sourceQueueURL, ReceiptHandle: receiptHandle}).promise()
} catch (err) {
console.error('Task: Deleting message from the ingest queue: ', err, 'deleteResp: ', deleteResp, 'receiptHandle: ', receiptHandle)
}
The mocked sqs.deleteMessage
is used for the first describe
block (i.e. I see the console.log
for it) and the test passes.
However, the mocked sqs.deleteMessage
function is not used for the second describe block (i.e. I do not see the console.log
message indicating the mocked function ran, and, in fact, I get a 5000ms timeout, indicating the real sqs.deleteMessage
was called (with invalid authorization, the deleteMessage
command takes >5 seconds).
I thought the
jest.restoreAllMocks()
in theafterAll
block of the first describe is restoring my mock. So I go with explicitly restoring theingestAndSaveFunctionSpy
mock withingestAndSaveFunctionSpy.mockRestore()
instead. Unfortunately, this results in the same behavior: the mocked AWS SDK is used in the firstdescribe
block, but it has been restored by theingestAndSaveFunctionSpy.mockRestore()
call.Just to test, I remove the
afterAll
in the firstdescribe
entirely. This results in the second test calling the mocked implementation ofingestAndSave
and thus the test failing.Declare the
jest.mock...
within eachdescribe
block, but this isn't allowed due tojest.mock
calls getting hoisted to the top of the file.
How can I mock a module using jest.mock(...
and have it persist between describe blocks while allowing mockRestore() calls to other mocked functions?
How to change mock implementation on a per single test basis? has me looking at mockImplemention
but I'm struggling to see how I'd implement it.
See related question attempting to tackle this from a different angle: How to have jest.mock persist between tests?