为 JavaScript 开玩笑的单元测试模拟 Secrets Manager 模块

Mocking Secrets Manager module for JavaScript jest unit tests

我在为开玩笑的单元测试模拟 AWS Secrets Manager 模块时遇到问题...它出错的部分是 .promise()。当我删除它时,代码对真正的 Secrets Manager 不起作用,所以我认为它需要留在那里。我如何模拟 getSecretData 函数,以便 getSecretData.promise() 可以为模拟工作?

这里是 SecretsManager.js 代码:

import AWS from 'aws-sdk';

export class SecretsManager {
  constructor() {
    AWS.config.update({
      region: 'us-east-1',
    });
    this.secretsManager = new AWS.SecretsManager();
  }

  async getSecretData(secretName) {
    try {
      const response = await this.secretsManager.getSecretValue({
        SecretId: secretName,
      }).promise();
      const secretString = response.SecretString;
      const parsedSecret = JSON.parse(secretString);
      return parsedSecret;
    } catch (e) {
      console.log('Failed to get data from AWS Secrets Manager.');
      console.log(e);
      throw new Error('Unable to retrieve data.');
    }
  }
}

这是 SecretsManager.test.js 代码:

import { SecretsManager } from '../utils/SecretsManager';

jest.mock('aws-sdk', () => {
  return {
    config: {
      update(val) {

      },
    },
    SecretsManager: function () {
      return {
        async getSecretValue({
          SecretId: secretName
        }) {
          return {
            promise: function () {
              return {
                 UserName: 'test',
                 Password: 'password',
              };
            }
          };
        }
      };
    }
  }

});


describe('SecretsManager.js', () => {
  describe('Given I have a valid secret name', () => {
    describe('When I send a request for test_creds', () => {
      it('Then the correct data is returned.', async () => {
        const mockReturnValue = {
          UserName: 'test',
          Password: 'password',
        };
        const logger = getLogger();
        const secretManager = new SecretsManager();
        const result = await secretManager.getSecretData('test_creds');
        expect(result).toEqual(mockReturnValue)
      });
    });
    describe('When I send a request without data', () => {
      it('Then an error is thrown.', async () => {
      const secretManager = new SecretsManager();
      await expect(secretManager.getSecretData()).rejects.toThrow();
      });
    });
  });
});

这是我在 运行 测试时得到的错误:

 this.secretsManager.getSecretValue(...).promise is not a function

非常感谢任何建议或指示!
谢谢你看我的 post.

我终于让它工作了...估计它会在 post 提出问题后不久发生,但我不会删除 post 我将分享我如何将模拟更改为让它发挥作用,以防它能帮助其他人。

注意:这只是更新的模拟,测试与上面的问题相同。

// I added this because it's closer to how AWS returns data for real.
const mockSecretData = {
  ARN: 'x',
  Name: 'test_creds',
  VersionId: 'x',
  SecretString: '{"UserName":"test","Password":"password"}',
  VersionStages: ['x'],
  CreatedDate: 'x'
}

jest.mock('aws-sdk', () => {
  return {
    config: {
      update(val) {
      },
    },
    SecretsManager: function () {
      return {
        getSecretValue: function ( { SecretId } ) {
          {
           // Adding function above to getSecretValue: is what made the original ".promise() is not a function" error go away.

            if (SecretId === 'test_creds') {
              return {
                promise: function () {
                  return mockSecretData;
                }
              };
            } else {
              throw new Error('mock error');
            }
        }
      }
    };
  }
}});

我运行也关注这个问题。可能有一种更优雅的方法来处理这个问题,它还允许更好的控制和断言,但我还没有找到。请注意,in-test 选项可能适用于较新版本的 Jest。

我个人通过使用手动模拟和 aws-sdk 的自定义模拟文件解决了这个问题。在您的情况下,它看起来类似于以下内容:

# app_root/__tests__/__mocks__/aws-sdk.js

const exampleResponse = {
  ARN: 'x',
  Name: 'test_creds',
  VersionId: 'x',
  SecretString: '{"UserName":"test","Password":"password"}',
  VersionStages: ['x'],
  CreatedDate: 'x'
};
const mockPromise = jest.fn().mockResolvedValue(exampleResponse);
const getSecretValue = jest.fn().mockReturnValue({ promise: mockPromise });
function SecretsManager() { this.getSecretValue = getSecretValue };
const AWS = { SecretsManager };

module.exports = AWS;

然后在你的测试文件中:

// ... imports

jest.mock('aws-sdk');

// ... your tests

所以,简而言之:

  • 您不是直接在测试文件中进行模拟,而是将模拟控制权交给模拟文件,Jest 知道在 __mocks__ 目录中查找该文件。
  • 您在 mock 文件
  • 中为 SecretsManager 创建一个 mock 构造函数
  • SecretsManager returns 具有模拟函数的实例 getSecretValue
  • getSecretValue returns 模拟承诺
  • 模拟承诺 returns exampleResponse

巴达繁荣,巴达 bing。 You can read more here.