我正在尝试开玩笑地模拟aws-sdk。实际上,我只关心一个功能。我怎样才能做到这一点?我已经阅读了有关开玩笑地嘲笑类的文档,但是文档很复杂,我不太理解。
这是我的最佳尝试:
handler.test.js
'use strict';
const aws = require('aws-sdk');
const { handler } = require('../../src/rotateSecret/index');
jest.mock('aws-sdk');
const event = {
SecretId: 'test',
ClientRequestToken: 'ccc',
Step: 'createSecret',
};
describe('rotateSecret', () => {
it.only('should not get or put a secret', async () => {
aws.SecretsManager.mockImplementation(() => ({
getSecretValue: () => ({}),
}));
expect.assertions(1);
await handler(event);
// You can see what I am trying to do here but it doesn't work
expect(aws.SecretsManager.getSecretManager).not.toHaveBeenCalled();
});
});
handler.js
exports.handler = async (event) => {
const secretsManager = new aws.SecretsManager();
const secret = await secretsManager.describeSecret({ SecretId: event.SecretId }).promise();
if (someCondition) {
console.log("All conditions not met");
return;
}
return secretsManager.getSecretValue(someParams)
};
答案 0 :(得分:2)
好的,我的处理方式如下:
为aws-sdk
创建一个实际的模拟并将其放在项目根目录的__mocks__/aws-sdk.js
文件中
// __mocks__/aws-sdk.js
class AWS {
static SecretsManager = class {
describeSecret = jest.fn(() =>{
return { promise: ()=> Promise.resolve({ ARN: "custom-arn1", Name: "describeSec" })}
});
getSecretValue = jest.fn(() =>{
return {promise: ()=> Promise.resolve({ ARN: "custom-arn2", Name: "getSecretVal" })
});
};
}
module.exports = AWS;
我在SecretsManager
之前使用了static,因为AWS
类从未实例化,但它希望访问SecretsManager
类。
在SecretsManager
内,我定义了2个函数,并使用jest.fn
对其进行了存根。
现在与您在测试文件中所做的相同的事情:
jest.mock('aws-sdk');
要测试您的模拟函数是否被调用,那就是棘手的部分(因此我将在本文末尾详细介绍)。
更好的方法是在完成所有处理后根据主函数的最终结果进行断言。
回到您的测试文件中,我将简单地使用await
调用处理程序(就像您已经拥有的那样),然后对最终结果进行断言,如下所示:
// test.js
describe("rotateSecret", () => {
it.only("should not get or put a secret", async () => {
const event = {name:"event"};
const result = await handler(event);
expect(result).toEqual("whatever-your-function-is-expected-to-return");
});
});
为此,您需要调整主handler.js
文件本身,并需要像这样从主函数主体中取出invocation of secrets Manager
:
const secretsManager = new aws.SecretsManager(); // <---- Declare it in outer scope
exports.handler = async (event) => {
const secret = await secretsManager
.describeSecret({ SecretId: event.SecretId })
.promise();
if (someCondition) {
console.log("All conditions not met");
return;
}
return secretsManager.getSecretValue(someParams);
};
然后回到您的test.js
文件中,您将需要类似地声明SecretsManager
调用,然后再启动处理程序函数,如下所示:
//test.js
describe("rotateSecret", () => {
const secretsManager = new aws.SecretsManager(); // <---- Declare it in outer scope
it.only("should not get or put a secret", async () => {
const event = {name:"event"};
await handler(event);
// Now you can make assertions on function invocations
expect(secretsManager.describeSecret).toHaveBeenCalled();
// OR check if passed args were correct
expect(secretsManager.describeSecret).toHaveBeenCalledWith({
SecretId: event.SecretId,
});
});
});
这将使您能够对函数调用以及所传递的参数进行断言。
我在函数范围之外声明它的原因是为了告诉Jest
secretsManager
应该存在于全局范围中的某个位置,并且应该从那里使用它。
以前,我们已经在函数范围内声明了它,因此Jest会调用它,但是我们无法访问它。
我们无法像这样AWS.SecretsManager.getSecretManager
那样直接引用它,因为getSecretManager
方法仅在实例化SecretsManager
类之后才可用(即使这样做,您也会得到一个新的类的实例,对任何断言都无济于事。
一个明显的问题是-您在每个调用中都使用了该函数,也许您不想这么做。
也许您只想对特定测试进行一次存根测试,但对于其余测试,则希望它正常运行。
在这种情况下,您不应创建__mocks__
文件夹。
相反,创建一次伪造的BUT,请确保您的SecretsManager
调用像以前一样在测试文件的外部范围内。
//test.js
const aws = require("aws-sdk");
describe("rotateSecret", () => {
// Declare it in outer scope
const secretsManager = new aws.SecretsManager();
it.only("should not get or put a secret", async () => {
const event = {name:"event"};
// Create a mock for this instance ONLY
secretsManager.describeSecret = jest.fn().mockImplementationOnce(()=>Promise.resolve("fake-values"));
await handler(event);
expect(secretsManager.describeSecret).toHaveBeenCalled();
expect(secretsManager.describeSecret).toHaveBeenCalledWith({
SecretId: event.SecretId,
});
});
});