我试图嘲笑AWS SDK。我有一个函数, intest
可以执行一些逻辑(包括对AWS SDK sqs.deletemessage
函数的一些调用),然后调用 ingestandsave
(哪个)还要调用 sqs.deletemessage
函数)。
- 测试
摄入
函数时,我想模拟我的 ingestandSave
函数,这样我就可以将这些测试纯粹关注 ingest
函数逻辑。
- 然后,我想在其描述块中测试
ingestandsave
函数逻辑 - 并测试真实的东西,而不是模拟版本。
- 测试
intest
或 ingestandSave
函数我需要使用模拟 sqs.deletemessage
我的最新尝试包括以下路径: https://www.lucasamos.dev/articles/jestmocking 。我的测试套件看起来像这样:
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 */
})
以上两个函数都通过看起来像这样的代码运行:
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)
}
模拟 sqs.deletemessage
用于第一个 descript> descript> descript
block(即请参阅 console.log
)和测试通过。
但是,模拟的 sqs.deletemessage
函数未用于第二个描述块(即ie i do 请参阅 console.log
meessage> Message>模拟函数运行,实际上,我获得了5000ms的超时,指出了Real sqs.deletemessage
被调用(有无效的授权, deetemessage
命令>命令take> 5秒 。
- ) 不幸的是,IngestandSaveFunctionspy 与
ingestandsavefunctionspy.mockrestore()
相反。它已通过 ingestandSaveFunctionspy.mockrestore()
呼叫。
-
只是为了进行测试,我将在第一个
中完全删除完全描述。这将导致第二个测试调用 ingestandSave
的模拟实现,从而失败。
-
在每个 jest.mock ...
中 descrip> descord 块中,但由于 jest.mock
调用呼叫,因此不允许这样做悬挂到文件的顶部。
我如何使用 jest.mock模拟模块(...
并在描述块之间持续存在,同时允许MockRestore()调用到其他模拟功能?
如何按单个测试基础更改模拟实现?我是否寻找 我如何实现它。
在模拟
中,我正在努力查看 72651692/如何实现测试中的方法 - 摩克 - 派对者“>如何让jest.mock在测试之间持续存在?
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 my ingestAndSave
function so I can have those tests focus purely on the ingest
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
or ingestAndSave
functions I need to use my mocked sqs.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 the afterAll
block of the first describe is restoring my mock. So I go with explicitly restoring the ingestAndSaveFunctionSpy
mock with ingestAndSaveFunctionSpy.mockRestore()
instead. Unfortunately, this results in the same behavior: the mocked AWS SDK is used in the first describe
block, but it has been restored by the ingestAndSaveFunctionSpy.mockRestore()
call.
-
Just to test, I remove the afterAll
in the first describe
entirely. This results in the second test calling the mocked implementation of ingestAndSave
and thus the test failing.
-
Declare the jest.mock...
within each describe
block, but this isn't allowed due to jest.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?
发布评论
评论(2)
如果您只想重置一个模拟,则不要重置所有模拟。只需重置间谍:
在外部范围内,我还建议使用A:
这将重置录音对您的AWS-SDK模拟模拟模拟,而无需实际更改任何模拟行为。否则,您可能会在执行另一个测试中实际发生的一个测试中验证行为。
在没有看到代码的情况下,很难提供更具体的建议,但是这种测试方式是代码气味,IMO。每当您必须在单位测试中监视自己的单位时,通常都是组织差的标志。尝试安排它,以使工作单位是谨慎的功能。作为一个抽象的示例,而不是将链接功能链接在一起,例如:
使它们谨慎,可测试的单元在外部功能中连接在一起:
现在您可以为
a
,b 和
c
独立而无需模拟自己的内部。If you only want to reset the one mock, then don't reset all of the mocks. Just reset the spy:
In the outer scope, I would also advise using a:
This will reset the recordings of the mock calls to your aws-sdk mock without actually changing any of the mocked behavior. Otherwise, you might be verifying behavior in one test that actually happened during the execution of another.
Without seeing the code, it's hard to offer more specific advice, but this style of test is a code smell, IMO. Any time you have to spy on your own unit in a unit test, it's usually a sign of poor organization. Try to arrange it so that discreet units of work are discreet functions. As an abstract example, instead of chaining functions together like:
Instead make them discreet, testable units that are joined together in the external function:
Now you can write tests for
a
,b
, andc
independently without having to mock your own internals.我通过将所有调用到AWS-SKD函数中的所有调用中解决了这个问题,然后嘲笑包装器功能。
因此,现在我在utils.js中有一个实用程序函数,看起来像这样:
在我的真实功能代码中,我称其为这种方式(而不是直接调用sqs.sendmessage-超级更改):
然后在测试中,我像这样模拟它 - 无论是在我的测试之前还是直接在我的测试之前:
冲洗,然后重复我要嘲笑的所有AWS -SDK功能。
I solved this by wrapping all calls to aws-skd functions in my own functions and then mocking my wrapper functions.
So now I have a utility functions in utils.js that looks like this:
And in my real function code, I call it this way (instead of calling sqs.sendMessage directly - a super-easy change):
And then during testing, I mock it like this - either in a beforeEach or directly before my test:
Later, rinse, and repeat for all aws-sdk functions I want to mock.