开Jest.mock函数在除其他模拟功能上被调用之后未使用

发布于 2025-02-08 09:57:37 字数 3986 浏览 4 评论 0 原文

我试图嘲笑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秒 。

  1. ) 不幸的是,IngestandSaveFunctionspy 与 ingestandsavefunctionspy.mockrestore()相反。它已通过 ingestandSaveFunctionspy.mockrestore()呼叫。

  2. 只是为了进行测试,我将在第一个中完全删除完全描述。这将导致第二个测试调用 ingestandSave 的模拟实现,从而失败。

  3. 在每个 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).

  1. 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.

  2. 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.

  3. 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

岁月静好 2025-02-15 09:57:37

如果您只想重置一个模拟,则不要重置所有模拟。只需重置间谍:

describe("FUNCTION: ingest", () => {
    let ingestAndSaveFunctionSpy

    beforeEach(()=> {
        ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
        Promise.resolve() )
    })

    // ...
    
    afterEach(() => {
        ingestAndSaveFunctionSpy.mockRestore()
    })
})

​​在外部范围内,我还建议使用A:

afterEach(() => {
    jest.clearAllMocks()
})

这将重置录音对您的AWS-SDK模拟模拟模拟,而无需实际更改任何模拟行为。否则,您可能会在执行另一个测试中实际发生的一个测试中验证行为。


在没有看到代码的情况下,很难提供更具体的建议,但是这种测试方式是代码气味,IMO。每当您必须在单位测试中监视自己的单位时,通常都是组织差的标志。尝试安排它,以使工作单位是谨慎的功能。作为一个抽象的示例,而不是将链接功能链接在一起,例如:

const a = (arg) => {
  const stuff = /* do some stuff with arg */
  b(stuff);
};

const b = (arg) => {
  const stuff = /* do some stuff with arg */
  c(stuff);
}

const c = (arg) => {
  /* do some stuff with arg */
}

使它们谨慎,可测试的单元在外部功能中连接在一起:

const abc = (arg) => {
  const firstStuff = a(arg);
  const secondStuff = b(firstStuff);
  c(secondStuff);
}

const a = (arg) => {
  return /* do some stuff with arg */
}

const b = (arg) => {
  return /* do some stuff with arg */
}

const c = (arg) => {
  /* do some stuff with arg */
}

现在您可以为 a b 和 c 独立而无需模拟自己的内部。

If you only want to reset the one mock, then don't reset all of the mocks. Just reset the spy:

describe("FUNCTION: ingest", () => {
    let ingestAndSaveFunctionSpy

    beforeEach(()=> {
        ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
        Promise.resolve() )
    })

    // ...
    
    afterEach(() => {
        ingestAndSaveFunctionSpy.mockRestore()
    })
})

In the outer scope, I would also advise using a:

afterEach(() => {
    jest.clearAllMocks()
})

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:

const a = (arg) => {
  const stuff = /* do some stuff with arg */
  b(stuff);
};

const b = (arg) => {
  const stuff = /* do some stuff with arg */
  c(stuff);
}

const c = (arg) => {
  /* do some stuff with arg */
}

Instead make them discreet, testable units that are joined together in the external function:

const abc = (arg) => {
  const firstStuff = a(arg);
  const secondStuff = b(firstStuff);
  c(secondStuff);
}

const a = (arg) => {
  return /* do some stuff with arg */
}

const b = (arg) => {
  return /* do some stuff with arg */
}

const c = (arg) => {
  /* do some stuff with arg */
}

Now you can write tests for a, b, and c independently without having to mock your own internals.

几度春秋 2025-02-15 09:57:37

我通过将所有调用到AWS-SKD函数中的所有调用中解决了这个问题,然后嘲笑包装器功能。

因此,现在我在utils.js中有一个实用程序函数,看起来像这样:

module.exports.sendMessage = async (params) => {  
    return await sqs.sendMessage(params).promise()
}

在我的真实功能代码中,我称其为这种方式(而不是直接调用sqs.sendmessage-超级更改):

await utils.sendMessage(sendMessageParams).catch(err => throw (err))

然后在测试中,我像这样模拟它 - 无论是在我的测试之前还是直接在我的测试之前:

sendMessageSpy = jest.spyOn(utils, 'sendMessage').mockReturnValue(Promise.resolve({success:true})) 

冲洗,然后重复我要嘲笑的所有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:

module.exports.sendMessage = async (params) => {  
    return await sqs.sendMessage(params).promise()
}

And in my real function code, I call it this way (instead of calling sqs.sendMessage directly - a super-easy change):

await utils.sendMessage(sendMessageParams).catch(err => throw (err))

And then during testing, I mock it like this - either in a beforeEach or directly before my test:

sendMessageSpy = jest.spyOn(utils, 'sendMessage').mockReturnValue(Promise.resolve({success:true})) 

Later, rinse, and repeat for all aws-sdk functions I want to mock.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文