如何用 jest 模拟 aws-sdk?

问题描述 投票:0回答:1

我正在尝试用玩笑来模拟 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)
};

class jestjs aws-sdk
1个回答
9
投票

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

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;

我之前使用过 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");
  });
});

测试 Secret Manager 的函数调用


为此,您需要调整主

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__/aws.js 假模块的缺点


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

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

在这种情况下,您不应创建

__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,
    });
  });
});

© www.soinside.com 2019 - 2024. All rights reserved.