0

次のように、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。私はここで完全にブロックされており、このアプローチがモックを解決するためのリードです

4

1 に答える 1

1

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/stackoverflow/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
于 2020-11-17T05:06:57.720 に答える