I have added a connection to Hashicorp vault (in a mapper service) to obtain a secret key to be used for encryption. I need to get the key in the constructor of my service. My tests are now failing because when the mapper service is instantiated the constructor gets called and the vault is unavailable. If I mock the entire service then all my other tests will not work. So I need to only mock the constructor call so it does not fire before every test.
mapper.service.ts
@Injectable()
export class Mapper {
key;
encryptionKey;
constructor(private vault: VaultService) {
this.setKey(); // I do not want this to happen in my tests
}
async setKey() {
this.vault.getValueFromVault('soapCredentials', 'encryptionKey').then(async (response) => {
this.encryptionKey = response;
console.log('this.encryptionKey', this.encryptionKey)
try {
this.key = (await promisify(scrypt)(this.encryptionKey, 'salt', 32)) as Buffer;
} catch {
throw new Error('Encryption set error')
}
}).catch( err => {
throw new Error(err);
});
}
mapper.service.spec.ts
describe('Mapper', () => {
let mapper: EnrichementMapper;
let vault: VaultService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
EnrichementMapper,
VaultService
],
}).compile();
mapper = module.get<EnrichementMapper>(EnrichementMapper);
vault = module.get<VaultService>(VaultService);
});
it('should be defined', () => {
expect(mapper).toBeDefined();
});
.... 20 more tests
Even the 'should be defined' test is failing because the constructor is being called and this.setKey() is being called. Which causes an error when it tries to run this.vault.getValueFromVault. How can I mock the constructor so that it does not fire?
CodePudding user response:
You are on the right track. What you want to do is ensure that your Vault is an injected service, as you have in the constructor. This way, you can provide a mock to replace it.
The easiest way I find to do this, is to then only define the functions I need in the mock, so that they return set data, and continue with your tests.
In your beforeEach()
, you replace the vault service with the Jest definition for the fake service.
describe('Mapper', () => {
let mapper: EnrichementMapper;
let vault: VaultService;
beforeEach(async () => {
const FakeVaultService = {
provide: VaultService,
useFactory: () => ({
setKey: jest.fn().mockResolvedValue('Key/Token, whatever'),
}),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
EnrichementMapper,
FakeVaultService
],
}).compile();
mapper = module.get<EnrichementMapper>(EnrichementMapper);
vault = module.get<VaultService>(VaultService);
});
it('should be defined', () => {
expect(mapper).toBeDefined();
});
.... 20 more tests
});
The FakeVaultService code is what is called then when your code calls the setKey. The mockResolvedValue makes it asynchronous, so you can have the proper code handling as well. Jest Mock reference