如何用玩笑嘲笑aws-sdk?

时间:2020-10-30 08:17:45

标签: class jestjs aws-sdk

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

1 个答案:

答案 0 :(得分:2)

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

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;

我在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");
  });
});

测试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__文件夹。

相反,创建一次伪造的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,
    });
  });
});