I have a simple setup where I'm calling a service method from the controller like so:
// companies.controller.ts
#Controller("company")
export class CompaniesController {
constructor(private companiesService: CompaniesService) {}
#Post("/:id/upload")
#UseInterceptors(FilesInterceptor("file"))
uploadFiles(
#Param("id") id: string,
#UploadedFiles() files: Array<Express.Multer.File>,
) {
// throw new HttpException("Company not found", HttpStatus.NOT_ACCEPTABLE);
// console.log(files);
try {
this.companiesService.uploadFiles(id, files);
console.log("didn't get error");
} catch (error) {
console.log("got error");
throw new HttpException("forbidden", HttpStatus.FORBIDDEN);
}
}
}
// companies.service.ts
#Injectable()
export class CompaniesService {
constructor(
private prisma: PrismaService,
private s3Service: S3Service,
private filesService: FilesService,
) {}
async uploadFiles(id: Company["id"], files: Array<Express.Multer.File>) {
const company = false // for testing
if (!company) {
console.log("Company not found");
throw new Error();
}
}
I'm running this by using nest start --watch.
When I call this endpoint, My app quits and I get the following logged to my console:
didn't get error
Company not found
/src/companies/companies.service.ts:54
throw new Error();
^
Error:
at CompaniesService.uploadFiles (/src/companies/companies.service.ts:54:13)
How come I can't catch the error I'm throwing in my controller? It's clearly not catching because it's logging company not found. Exception Filtering is supposed to be built in by default so I'm not sure why this isn't producing a 500?
You aren't awaiting the this.companiesService.uploadFiles(id, files); call, which means that it is an unhandled promise. If anything errors from it, it's an unhandled rejection. Just add async to the uploadFiles in the controller and await to thethis.companiesService.uploadFiles call and you should be good to go
Related
I have typescript class with some functions in it. Each function has a try catch block where upon hitting the catch it returns a pre defined response.
I am writing unit tests using mocha and chai and I am having trouble trying to explicitly hit the catch blocks.
For example, consider this simple function below
public async verifyCode(email: string, code: string) {
try {
let result: CodeInterface | null = //call db to get matching code
if(result === null) {
return {status: 401, message: INCORRECT_FELLOWSHIP_CODE_MESSAGE};
}
return result._id;
} catch (error) {
Logger.log(LoggerLogTypes.ERROR, {
class_name: "LaunchServiceImpl",
function_name: "verifyFellowshipCode",
message: error.message,
stack: error.stack,
additional_info: {
code
}
});
return false;
}
}
I want to write a test case where I can just send the control directly to the catch block to get the false value. This is a very simplistic example but in few other functions I am doing a lot more in the catch block.
My mocha unit test looks like this:
it("should go to the catch block and return false in case of exception", async function() {
let serviceInstance = new MyClass();
let response = await serviceInstance.verifyCode("john#test.com", "abc123");
// how do I directly jump to the catch block here??
expect(response).to.equal(false);
});
Suppose you have a function that will throw an error with the message User not found so you can test like this:
profileServiceInstance.getProfile('1').catch((err) => {
expect(() => {
throw err;
}).to.throw(Error, 'User not found');
});
class AuthController {
static methods = {
GET: {
'/auth/signup': {
func: AuthService.signUp,
response: (data, res) => {
res.statusCode = 200;
res.end(JSON.stringify(data));
},
},
},
};
static use(req, res) {
const route = this.methods[req.method][req.url];
if (!route) {
res.statusCode = 404;
res.end(JSON.stringify({ message: 'Not found 404!' }));
return;
}
try {
const data = JSON.parse(req?.body?.data || '{}');
const result = route.func({ ...data });
route.response(result, res);
} catch (err) {
console.log(err, 'here');
res.statusCode = err.statusCode || 500;
res.end(JSON.stringify(err.message));
}
}
}
class AuthService {
static async signUp({ login, password }) {
if (!login || !password) throw new BaseError(400, 'kl', 'Custom error');
}
}
It shows the error in console but try catch block doesn't see it.
Here is the traceback.
I don't know what the reason is because the function which throws error is inside of the block. Help please!
The trace back that you attached tells you exactly what the problem is and what you need to do:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch()
You can't catch an exception thrown by an async function with a try..catch block outside of that function, because script execution reaches the catch block before the async execution is finished. You therefor have to use .catch(..) instead:
const result = route.func({ ...data }).catch((err) => {
console.log("catched error: ", err);
});
I see one issue. You have declared signUp() to be async. That means it always returns a promise and it means that any throw operations inside it reject that promise that it returns (the exception doesn't propagate synchronously). But, when you attempt to call it here:
const result = route.func({ ...data });
You don't await it so when signUp() rejects, the promise goes into result, but nobody ever handles the fact that the promise rejected and you get UnhandlePromiseRejectionWarning from the system.
I can't see the whole overall design (of all the other routes), but perhaps you just need to add await to this:
const result = await route.func({ ...data });
And, you would have to make .use() be async also.
Or, if signUp() doesn't actually need to be async, then just remove the async from its declaration and the throw will be synchronous (instead of being turned into a rejected promise) and your try/catch will catch it then.
I have a function like this:
async getPatient(patientId: string): Promise<PatientDTO> {
const patient = await PatientDAO.getPatients({ id: patientId })
if (patient.length === 0) {
throw new NotFoundError("Patient Not Found!")
}
return patient[0]
}
But I got an error
UnhandledPromiseRejectionWarning: Error: Patient Not Found!
This happened cause I have used async function. How can I make this code running properly?
In order to manage errors in an async function, you have to use a try/catch block:
async getPatient(patientId: string): Promise<PatientDTO> {
try {
const patient = await PatientDAO.getPatients({ id: patientId })
return patient[0]
} catch (error) {
// Do whatever you may want with error
throw error;
}
}
I should mention, that if you simply want to throw the error thats received from getPatients theres no need for a try/catch block at all. Its only needed if you wish to modify the error or perform an extra action according to the error that was thrown.
You have 2 options:
First one is try/catch block with await keyword. Please notice that await has to be used in async function.
try {
const patient = await getPatient(foo);
// handle your data here
} catch(e) {
// error handling here
}
Second one is catch function
getPatient(foo)
.then(patient => {
// handle your data here
}).catch(error => {
// error handling here
});
i have handle exception in adonis but the message is not can not change base condition
what i expected is i can change message error in any controller
'use strict'
const { LogicalException } = require('#adonisjs/generic-exceptions')
class InvalidAccessException extends LogicalException {
/**
* Handle this exception by itself
*/
handle (error, { response }) {
return response.status(403).json({
error: error,
});
}
}
module.exports = InvalidAccessException
and in my controller if there is error i just trow error
throw new InvalidAccessException("user already register");
but the error message is always empty
I'm having issues with mocked functions that are passing a class as a parameter. For some reason, it doesn't seem to assert the content correctly
// ErrorHandler.js
class ErrorHandler extends Error {
constructor(status, reason) {
super();
this.status = status;
this.reason = reason;
}
}
export {
ErrorHandler
}
// express file express.js
const method = async (req, res, next) => {
try {
throw new ErrorHandler(401, 'error')
next()
} catch (error) {
next(error)
}
}
// test file
it('should call the next method with the proper error', async () => {
const request = {
body: {}
}
const next = jest.fn()
const response = mockResponse() // here it's just a simple mock
await method(request, response, next)
expect(next).toHaveBeenCalledWith(
// here the problem is that it doesn't seem to assert the parameters
// and this test is passing
new ErrorHandler('random text')
)
})
I tried mocking the ErrorHandler class but then It gives another error related that it can't compare the next method anymore
The problem is that Jest is trying to compare two error objects and doesn't really know how. You can see this with a simple assertion:
expect(new ErrorHandler(404, 'not found')).not.toEqual(new ErrorHandler(401, 'unauthorized'))
with the result:
expect(received).not.toEqual(expected) // deep equality
Expected: not [Error]
You need to be more specific, e.g.:
expect(next).toHaveBeenCalled();
const [err] = next.mock.calls[0];
expect(err).toMatchObject({ status: 401, reason: 'error' });