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
ingestfunction, I want to mock myingestAndSavefunction so I can have those tests focus purely on theingestfunction logic. - Then I want to test the
ingestAndSavefunction logic in its own describe block - and test the real thing, not the mocked version. - When testing either
ingestoringestAndSavefunctions 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 theafterAllblock of the first describe is restoring my mock. So I go with explicitly restoring theingestAndSaveFunctionSpymock withingestAndSaveFunctionSpy.mockRestore()instead. Unfortunately, this results in the same behavior: the mocked AWS SDK is used in the firstdescribeblock, but it has been restored by theingestAndSaveFunctionSpy.mockRestore()call.Just to test, I remove the
afterAllin the firstdescribeentirely. This results in the second test calling the mocked implementation ofingestAndSaveand thus the test failing.Declare the
jest.mock...within eachdescribeblock, but this isn't allowed due tojest.mockcalls 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?