如何用 jest 模拟 aws-sdk?

use*_*776 2 class jestjs aws-sdk

我正在尝试用玩笑来模拟 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();
  });
});
Run Code Online (Sandbox Code Playgroud)

处理程序.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)
};

Run Code Online (Sandbox Code Playgroud)

nis*_*ush 8

好的,我的处理方法如下:

AWS-SDK 模拟


创建一个实际的模拟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;
Run Code Online (Sandbox Code Playgroud)

我以前使用过 static SecretsManager,因为AWS类从未实例化,但它想要访问SecretsManager类。

在里面SecretsManager,我定义了 2 个函数并使用jest.fn.

现在与您在测试文件中所做的内容相同:

jest.mock('aws-sdk');
Run Code Online (Sandbox Code Playgroud)

如何测试


要测试您的模拟函数是否被调用,这是棘手的部分(所以我将在本文末尾详细介绍这一点)。

更好的方法是在所有处理完成后对主函数的最终结果进行断言。

断言


回到您的测试文件,我只需使用await(正如您已经拥有的那样)调用处理程序,然后对最终结果进行断言,如下所示:

jest.mock('aws-sdk');
Run Code Online (Sandbox Code Playgroud)

测试 Secret Manager 的函数调用


为此,您需要调整主文件本身,并且需要从主函数体中handler.js取出,如下所示:invocation of secrets Manager

// 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");
  });
});
Run Code Online (Sandbox Code Playgroud)

然后回到文件中,在启动处理程序函数之前,test.js您需要类似地声明调用,如下所示:SecretsManager

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);
};
Run Code Online (Sandbox Code Playgroud)

这将允许您对函数调用以及传递的参数进行断言。

我在函数作用域之外声明它的原因是告诉 Jest 它secretsManager应该存在于全局作用域中的某个位置,并且应该从那里使用它。

以前,我们在函数作用域内声明了它,因此 Jest 会调用它,但我们无法访问它。

我们不能像这样直接引用它,AWS.SecretsManager.getSecretManager因为getSecretManager方法仅在实例化该类后才可用SecretsManager(即使您这样做,您也会获得该类的一个新实例,这对任何断言都没有帮助)。

__mocks__/aws.js 假模块的缺点


明显的问题是 - 你在每次调用时都对函数进行存根处理,也许你不希望这样。

也许您只想针对特定测试将其存根一次,但对于其余测试,您希望它正常运行。

在这种情况下,您不应创建__mocks__文件夹。

相反,创建一个一次性的假文件,但确保您的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,
    });
  });
});
Run Code Online (Sandbox Code Playgroud)