开玩笑 Google Secret Manager 客户端坏了

jest mocking for Google Secret Manager client is broken

我正在尝试从 GCP Secret Manager 获取机密,如下所示:

import { SecretManagerServiceClient } from '@google-cloud/secret-manager';

const getSecrets = async (storeId) => {
  try {
    const client = new SecretManagerServiceClient();
    const [accessReponse] = await client.accessSecretVersion({
      name: `projects/messaging-service-dev-b4f0/secrets/STORE_${storeId}_MESSANGER_CHANNEL_TOKEN/versions/latest`,
    });
    const responsePayload = accessReponse.payload.data.toString();
    return responsePayload;
  } catch (error) {
    console.error(`getSecretInfo: ${error.message}`);
    throw error;
  }
};

export default getSecrets;

为了这个功能,为了编写单元,我需要模拟 SecretManagerServiceClient 和它的功能 accessSecretVersion 所以跟进我为此编写的代码。

import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
import { mocked } from 'ts-jest/utils';

import getSecrets from './../get-secrets';

jest.mock('@google-cloud/secret-manager');

const mockAccessSecretVersion = jest.fn().mockReturnValue({
    promise: jest.fn().mockResolvedValue({
        accessReponse: {
            payload: {
                data: 'secret'
            }
        }
    })
})

describe('getSecrets', () => {
    beforeEach(() => {
      jest.clearAllMocks();
      console.error = jest.fn();
    });
  
    it('should console log correct message if token exist on global', async () => {
        const mock = mocked(await (new SecretManagerServiceClient()).accessSecretVersion);
        mock.mockImplementationOnce(() => 'sa') 
        const factory = await getSecrets('1');
        jest.spyOn(global.console, 'error'); 
    });
  
  });
  

这里,测试被破坏为 TypeError: (intermediate value) is not iterable,我在这里完全被阻止了,这种方法解决模拟的任何线索

此错误意味着您没有正确模拟 client.accessSecretVersion 方法的解析值。这是一个完整的单元测试解决方案:

get-secrets.ts:

import { SecretManagerServiceClient } from '@google-cloud/secret-manager';

const getSecrets = async (storeId) => {
  try {
    const client = new SecretManagerServiceClient();
    const [accessReponse] = await client.accessSecretVersion({
      name: `projects/messaging-service-dev-b4f0/secrets/STORE_${storeId}_MESSANGER_CHANNEL_TOKEN/versions/latest`,
    });
    const responsePayload = accessReponse.payload!.data!.toString();
    return responsePayload;
  } catch (error) {
    console.error(`getSecretInfo: ${error.message}`);
    throw error;
  }
};

export default getSecrets;

get-secrets.test.ts:

import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
import getSecrets from './get-secrets';

const accessReponse = {
  payload: { data: { name: 'teresa teng' } },
};
const mClient = {
  accessSecretVersion: jest.fn(),
};

jest.mock('@google-cloud/secret-manager', () => {
  const mSecretManagerServiceClient = jest.fn(() => mClient);
  return { SecretManagerServiceClient: mSecretManagerServiceClient };
});

describe('getSecrets', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });
  afterAll(() => {
    jest.resetAllMocks();
    jest.restoreAllMocks();
  });

  it('should console log correct message if token exist on global', async () => {
    mClient.accessSecretVersion.mockResolvedValueOnce([accessReponse]);
    const actual = await getSecrets('1');
    expect(actual).toEqual({ name: 'teresa teng' }.toString());
    expect(SecretManagerServiceClient).toBeCalledTimes(1);
    expect(mClient.accessSecretVersion).toBeCalledWith({
      name: `projects/messaging-service-dev-b4f0/secrets/STORE_1_MESSANGER_CHANNEL_TOKEN/versions/latest`,
    });
  });

  it('should log error and rethrow it', async () => {
    const errorLogSpy = jest.spyOn(console, 'error');
    const mError = new Error('network');
    mClient.accessSecretVersion.mockRejectedValueOnce(mError);
    await expect(getSecrets('1')).rejects.toThrowError('network');
    expect(SecretManagerServiceClient).toBeCalledTimes(1);
    expect(mClient.accessSecretVersion).toBeCalledWith({
      name: `projects/messaging-service-dev-b4f0/secrets/STORE_1_MESSANGER_CHANNEL_TOKEN/versions/latest`,
    });
    expect(errorLogSpy).toBeCalledWith('getSecretInfo: network');
  });
});

单元测试结果:

 PASS  src/Whosebug/64857093/get-secrets.test.ts (13.322s)
  getSecrets
    ✓ should console log correct message if token exist on global (6ms)
    ✓ should log error and rethrow it (9ms)

  console.error node_modules/jest-environment-jsdom/node_modules/jest-mock/build/index.js:866
    getSecretInfo: network

----------------|----------|----------|----------|----------|-------------------|
File            |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files       |      100 |      100 |      100 |      100 |                   |
 get-secrets.ts |      100 |      100 |      100 |      100 |                   |
----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        14.744s