开玩笑 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
我正在尝试从 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