I'm currently attempting to mock AWS SecretsManager for my unit testing with Jest, and everytime I'm hit with the ConfigError
My code is somewhat like this
//index.ts
import SM from "aws-sdk/clients/secretsmanager"
const secretManagerClient = new SM()
...
export const randomMethod = async (a: string, b: string) => {
let secret
const personalToken = {
SecretId: process.env.secretId,
}
secretManagerClient
.getSecretValue(personalToken, (err, data) => {
if (err) {
console.error(`[SECRETS MANAGER] Error fetching personal token : ${err}`)
} else if (data && data.SecretString) {
secret = data.SecretString
}
})
}
My mock goes like this :
//index.test.js
const mockGetSecretValue = jest.fn((SecretId) => {
switch (SecretId) {
case process.env.GITHUB_PERSONAL_TOKEN:
return {
SecretString: process.env.GITHUB_PERSONAL_TOKEN_VALUE,
}
default:
throw Error("secret not found")
}
})
jest.mock("aws-sdk/clients/secretsmanager", () => {
return jest.fn(() => {
return {
getSecretValue: jest.fn(({ SecretId }) => {
return mockGetSecretValue(SecretId)
}),
promise: jest.fn(),
}
})
})
However, I get this error thrown at me : ConfigError: Missing region in config
, which I understand to some extent, however I don't understand why it occurs here in the mocking part...
Thanks in advance!
EDIT: Thanks to the 1st answer, I've managed to stop having this error. However, the getSecretValue()
method is not returning the Secret value I want.
CodePudding user response:
You should NOT use the callback of .getSecretValue()
method with .promise()
together. Just choose one of them. The error means you didn't mock the secretsmanager
class correctly of aws-sdk
.
E.g.
index.ts
:
import SM from 'aws-sdk/clients/secretsmanager';
const secretManagerClient = new SM();
export const randomMethod = async () => {
const personalToken = {
SecretId: process.env.secretId || '',
};
try {
const data = await secretManagerClient.getSecretValue(personalToken).promise();
return data.SecretString;
} catch (err) {
console.error(`[SECRETS MANAGER] Error fetching personal token : ${err}`);
}
};
index.test.ts
:
import { randomMethod } from '.';
import SM from 'aws-sdk/clients/secretsmanager';
import { mocked } from 'ts-jest/utils';
import { PromiseResult } from 'aws-sdk/lib/request';
jest.mock('aws-sdk/clients/secretsmanager', () => {
const mSecretManagerClient = {
getSecretValue: jest.fn().mockReturnThis(),
promise: jest.fn(),
};
return jest.fn(() => mSecretManagerClient);
});
describe('69977310', () => {
test('should get secret value', async () => {
process.env.secretId = 's1';
const mSecretManagerClient = mocked<InstanceType<typeof SM>>(new SM());
const mGetSecretValueRequest = mocked(mSecretManagerClient.getSecretValue());
mGetSecretValueRequest.promise.mockResolvedValue({
SecretString: JSON.stringify({ password: '123456' }),
} as PromiseResult<any, any>);
const actual = await randomMethod();
expect(actual).toEqual(JSON.stringify({ password: '123456' }));
expect(mSecretManagerClient.getSecretValue as jest.Mocked<any>).toBeCalledWith({ SecretId: 's1' });
});
test('should throw error', async () => {
process.env.secretId = 's1';
const logSpy = jest.spyOn(console, 'error').mockImplementation(() => 'suppress error log for testing');
const mSecretManagerClient = mocked<InstanceType<typeof SM>>(new SM());
const mGetSecretValueRequest = mocked(mSecretManagerClient.getSecretValue());
const mError = new Error('network');
mGetSecretValueRequest.promise.mockRejectedValue(mError);
await randomMethod();
expect(logSpy).toBeCalledWith(`[SECRETS MANAGER] Error fetching personal token : ${mError}`);
expect(mSecretManagerClient.getSecretValue as jest.Mocked<any>).toBeCalledWith({ SecretId: 's1' });
});
});
test result:
PASS examples/69977310/index.test.ts (7.722 s)
69977310
✓ should get secret value (4 ms)
✓ should throw error (1 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
index.ts | 100 | 50 | 100 | 100 | 6
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.282 s, estimated 10 s
package versions:
"aws-sdk": "^2.875.0",
"typescript": "^4.1.2",
"jest": "^26.6.3",
CodePudding user response:
I've overlooked the fact that I was using a callback in order to bypass the promise()
.
The following is the correct code:
const mockGetSecretValue = jest.fn((SecretId, callback) => {
console.log("secretId", SecretId)
switch (SecretId) {
case process.env.GITHUB_PERSONAL_TOKEN:
const data = {
SecretString: process.env.GITHUB_PERSONAL_TOKEN_VALUE,
}
callback(null, data)
break;
default:
const err = new Error("secret not found")
throw err
}
})
jest.mock("aws-sdk/clients/secretsmanager", () => {
return jest.fn(() => {
return {
promise: jest.fn(),
getSecretValue: jest.fn(({ SecretId }, callback) => {
return mockGetSecretValue(SecretId, callback)
}),
}
})
})
Thanks again for your help @slideshowp2!