What I'm trying to do is test that the error is caught and returned with the correct status code when a use case function returns a rejected promise.
The problem I'm having is that instead of a rejected promise, i just get this error instead of the mock function just returning a rejected promise.
This is the error that I'm getting:
TypeError: Cannot read properties of undefined (reading 'then')
Inside a test suite I have this setup:
const container = new Container();
describe('Controller Error Tests', () => {
let reportController: ReportController;
let generateReportUseCase: IGenerateReportUseCase = mock<IGenerateReportUseCase>();
container.bind<ReportServiceLocator>(TYPES.ReportServiceLocator).to(ReportServiceLocator);
beforeAll(async () => {
jest.clearAllMocks();
cleanUpMetadata();
dotenv.config();
reportController = new ReportController(container.get<ReportServiceLocator>(Symbol.for("ReportServiceLocator")));
});
it('User held shares report use case throws error', async () => {
let requestObj = httpMocks.createRequest({
cookies: {
token: jwt.sign({ id: 'test' }, process.env.JWT_SECRET_KEY!),
},
query: {
report_type: 'CSV'
}
});
let responseObj = httpMocks.createResponse();
mock(generateReportUseCase).usersHeldShares.mockRejectedValue(new Error('Could not generate report'));
await reportController.userHeldShares(requestObj, responseObj);
expect(responseObj.statusCode).toEqual(500);
})
})
reportController.userHeldShares
is an inversify controller that looks like this:
@httpGet('/held-shares')
public async userHeldShares(@request() req: express.Request, @response() res: express.Response){
let jwtSecretKey = process.env.JWT_SECRET_KEY;
let ascending: boolean = req.query.ascending === "false" ? false : true;
let report_type: string = String(req.query.reportformat);
let cookieData = await <IUserDto>jwt.verify(req.cookies.token, jwtSecretKey!);
if(!cookieData.id){
return res.status(401).json({error: 'User not authorised'});
}
return await this.generateReportUseCase.usersHeldShares(cookieData.id!, ascending, report_type)
.then((userDto: IUserDto) => {
res.status(200).json(userDto)
})
.catch((err: Error) => {
res.status(500).json(err);
});
}
Here is what the first few lines of generateReportUseCase.usersHeldShares
looks like, which is where the error comes from:
usersHeldShares(user_id: string, ascending: boolean, report_type: string): Promise<IUserDto> {
return new Promise(async (resolve, reject) => {
this.tradeReadOnlyRepository.fetch({user_id: user_id}, false)
.then(async trades => {
When it hits the line return await this.generateReportUseCase.usersHeldShares(cookieData.id!, ascending, report_type)
in the inversify controller it just returns a rejected promise without entering the logic of the actual function.
It turns out I was still using a service locator for the proper implementation of the report generator. I replaced this with a TestServiceLocator
which will now contain all of the mock return values.
The implementation of this is like so:
container.bind<TestServiceLocator>(TYPES.ReportServiceLocator).to(TestServiceLocator);
@injectable()
export default class TestServiceLocator {
constructor(@inject(TYPES.IStockReadOnlyRepository) private stockReadRepository: IStockReadOnlyRepository,
@inject(TYPES.IStockWriteOnlyRepository) private stockWriteRepository: IStockWriteOnlyRepository,
@inject(TYPES.IUserWriteOnlyRepository) private userWriteRepository: IUserWriteOnlyRepository,
@inject(TYPES.IUserReadOnlyRepository) private userReadRepository: IUserReadOnlyRepository,
@inject(TYPES.ITradeReadOnlyRepository) private tradeReadRepository: ITradeReadOnlyRepository,
@inject(TYPES.ITradeWriteOnlyRepository) private tradeWriteRepository: ITradeWriteOnlyRepository){}
public GetGenerateReportUseCase(): IGenerateReportUseCase {
let generateReportUseCase: IGenerateReportUseCase = mock<IGenerateReportUseCase>();
mock(generateReportUseCase).completeStockValues.mockRejectedValue(new Error('Could not generate report'));
mock(generateReportUseCase).selectedCompanyDetails.mockRejectedValue(new Error('Could not generate report'));
mock(generateReportUseCase).usersHeldShares.mockRejectedValue(new Error('Could not generate report'));
return generateReportUseCase;
}
public GetDownloadReportUseCase(): IDownloadReportUseCase {
let downloadReportUseCase: IDownloadReportUseCase = mock<IDownloadReportUseCase>();
mock(downloadReportUseCase).invoke.mockRejectedValue(new Error('Could not get report data'));
return downloadReportUseCase;
}
}