Reputation: 59
How do I test the following javascript module code? My test passes but I can't even cover the Promise.all or any code (trying to do one step at a time), I have tried to follow the advice in How to set a test for multiple fetches with Promise.all using jest
const dbSession = require('server/db/dbSessionModule');
jest.mock('server/db/dbSessionModule');
let controller = {
post: (request, response) => {
const dbSession = new dbSessionModule();
const httpResponse = new HttpResponse();
Promise.all([bcrypt.hash(request.body.payload.password, config.saltRounds), dbSession.connect()])
.then(promiseValues => {
return signupModule.createAccount(dbSession, request.body.payload, promiseValues[0]);
})
.then(responsePayload => {
httpResponse.setSuccess(201, 'ACCOUNT_CREATED', responsePayload);
response.status(httpResponse.status).json(httpResponse);
})
.catch(error => {
console.log(error);
httpResponse.setReject(500, 'ACCOUNT_CREATION_ERROR', error);
response.status(httpResponse.status).json(httpResponse);
})
.finally(() => {
dbSession.release();
});
}
};
describe('Signup Controller', function() {
it('Should hash password and connect user', () => {
const passowrd = 'test';
const hashPassword = '3rasf4#4ad$@!';
const bcrypt = {
hash: jest.fn().mockImplementation(function() {
return Promise.resolve(hashPassword);
})
};
return expect(Promise.all([bcrypt.hash(passowrd, 5), dbSession.connect()])).resolves.toEqual([hashPassword, {}]);
});
});
This is my coverage report:
Upvotes: 4
Views: 2010
Reputation: 102567
Here is a TypeScript solution.
To convert to JavaScript replace (dbSessionModule as jest.Mocked<DbSessionModule>)
with dbSessionModule
, (bcrypt as jest.Mocked<typeof bcrypt>)
with bcrypt
, and (signupModule as jest.Mocked<typeof signupModule>)
with signupModule
. Replace all references to .ts
with .js
.
I emulated your imported modules and the directory structure is:
.
├── HttpResponse.ts
├── bcrypt.ts
├── config.ts
├── dbSessionModule.ts
├── index.spec.ts
├── index.ts
└── signupModule.ts
index.ts
, the file will be tested
import HttpResponse from './HttpResponse';
import DbSessionModule from './dbSessionModule';
import bcrypt from './bcrypt';
import signupModule from './signupModule';
import config from './config';
const controller = {
post: (request, response) => {
const dbSession = new DbSessionModule();
const httpResponse = new HttpResponse();
return Promise.all([bcrypt.hash(request.body.payload.password, config.saltRounds), dbSession.connect()])
.then(promiseValues => {
return signupModule.createAccount(dbSession, request.body.payload, promiseValues[0]);
})
.then(responsePayload => {
httpResponse.setSuccess(201, 'ACCOUNT_CREATED', responsePayload);
response.status(httpResponse.status).json(httpResponse);
})
.catch(error => {
console.log(error);
httpResponse.setReject(500, 'ACCOUNT_CREATION_ERROR', error);
response.status(httpResponse.status).json(httpResponse);
})
.finally(() => {
dbSession.release();
});
}
};
export default controller;
Unit test index.spec.ts
:
import '@babel/polyfill';
import controller from './';
import DbSessionModule from './dbSessionModule';
import bcrypt from './bcrypt';
import signupModule from './signupModule';
import config from './config';
import HttpResponse from './HttpResponse';
const request = {
body: {
payload: {
password: ''
}
}
};
const response = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
const HttpResponseMocked = {
setSuccess: jest.fn().mockImplementation(status => {
HttpResponseMocked.status = status;
}),
setReject: jest.fn().mockImplementation(status => {
HttpResponseMocked.status = status;
}),
status: 0
};
jest.mock('./HttpResponse.ts', () => {
return jest.fn(() => HttpResponseMocked);
});
jest.mock('./bcrypt.ts');
const DbSessionModuleMocked = {
connect: jest.fn(),
release: jest.fn()
};
jest.mock('./dbSessionModule.ts', () => {
return jest.fn(() => DbSessionModuleMocked);
});
jest.mock('./signupModule.ts');
const dbSessionModule = new DbSessionModule();
const httpResponse = new HttpResponse();
describe('controller', () => {
describe('#post', () => {
request.body.payload.password = '123';
config.saltRounds = 'mocked salt';
beforeEach(() => {
(dbSessionModule as jest.Mocked<DbSessionModule>).connect.mockReset();
(dbSessionModule as jest.Mocked<DbSessionModule>).release.mockReset();
});
it('create account correctly', async () => {
(bcrypt as jest.Mocked<typeof bcrypt>).hash.mockResolvedValueOnce('mocked hash');
(dbSessionModule as jest.Mocked<DbSessionModule>).connect.mockResolvedValueOnce({});
(signupModule as jest.Mocked<typeof signupModule>).createAccount.mockResolvedValueOnce({ accountId: 1 });
await controller.post(request, response);
expect(bcrypt.hash).toBeCalledWith('123', 'mocked salt');
expect(dbSessionModule.connect).toBeCalledTimes(1);
expect(signupModule.createAccount).toBeCalledWith(dbSessionModule, { password: '123' }, 'mocked hash');
expect(httpResponse.setSuccess).toBeCalledWith(201, 'ACCOUNT_CREATED', { accountId: 1 });
expect(response.status).toBeCalledWith(201);
expect(response.json).toBeCalledWith(httpResponse);
expect(dbSessionModule.release).toBeCalledTimes(1);
});
it('create account error', async () => {
(bcrypt as jest.Mocked<typeof bcrypt>).hash.mockResolvedValueOnce('mocked hash');
(dbSessionModule as jest.Mocked<DbSessionModule>).connect.mockResolvedValueOnce({});
(signupModule as jest.Mocked<typeof signupModule>).createAccount.mockRejectedValueOnce(new Error('some error'));
await controller.post(request, response);
expect(bcrypt.hash).toBeCalledWith('123', 'mocked salt');
expect(dbSessionModule.connect).toBeCalledTimes(1);
expect(signupModule.createAccount).toBeCalledWith(dbSessionModule, { password: '123' }, 'mocked hash');
expect(httpResponse.setReject).toBeCalledWith(500, 'ACCOUNT_CREATION_ERROR', new Error('some error'));
expect(response.status).toBeCalledWith(500);
expect(response.json).toBeCalledWith(httpResponse);
expect(dbSessionModule.release).toBeCalledTimes(1);
});
});
});
Unit test result and coverage:
PASS src/stackoverflow/56924299/index.spec.ts
controller
#post
✓ create account correctly (10ms)
✓ create account error (8ms)
console.log src/stackoverflow/56924299/index.ts:595
Error: some error
at Object.<anonymous> (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56924299/index.spec.ts:80:94)
at step (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56924299/index.spec.ts:32:23)
at Object.next (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56924299/index.spec.ts:13:53)
at /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56924299/index.spec.ts:7:71
at new Promise (<anonymous>)
at Object.<anonymous>.__awaiter (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56924299/index.spec.ts:3:12)
at Object.<anonymous> (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56924299/index.spec.ts:77:32)
at Object.asyncJestTest (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
at resolve (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
-----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files | 85.19 | 100 | 71.43 | 85.19 | |
bcrypt.ts | 50 | 100 | 0 | 50 | 2,3 |
config.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
signupModule.ts | 50 | 100 | 0 | 50 | 2,3 |
-----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 1.414s, estimated 3s
The completed TypeScript demo is here
Test coverage report:
Upvotes: 1