我正在尝试用玩笑来模拟 aws-sdk。其实我只关心一个功能。我该怎么做?我已经阅读了有关用笑话模拟类的文档,但文档很复杂,我不太理解它们。
这是我最好的尝试:
处理程序.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();
});
});
处理程序.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)
};
好的,我的处理方法如下:
为
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;
我之前使用过 static
SecretsManager
,因为 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__
文件夹。
相反,创建一个一次性的假文件,但确保您的
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,
});
});
});