[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Setting up Exception Filter by extending from BaseExceptionFilter will fail E2E tests setup using a testing module.
I've been trying to set up an exception filter on the controller to map domain-level errors to their HttpException
counterparts.
I set up the exception filter as follows:
@Catch()
export class HttpExceptionFilter extends BaseExceptionFilter {
constructor(@Inject(HTTP_SERVER_REF) applicationRef: HttpServer) {
super(applicationRef);
}
catch(error: Error, host: ArgumentsHost) {
switch (error.name) {
case 'UserNotFoundError':
return super.catch(new BadRequestException('User not found'), host);
default:
return super.catch(error, host);
}
}
}
and introduced it into the controller:
@Get()
@UseFilters(HttpExceptionFilter)
root(): string {
throw new UserNotFoundError();
}
This solution works on the actual server but fails during E2E tests with the follwing error:
TypeError: this.applicationRef.reply is not a function
17 | switch (error.name) {
18 | case 'UserNotFoundError':
> 19 | return super.catch(new BadRequestException('User not found'), host);
| ^
20 |
21 | default:
22 | return super.catch(error, host);
It should be possible to extend BaseExceptionFilter
and test its functionality.
https://github.com/mpontus/nest-exception-filter-example
yarn install
yarn test:e2e
to observe the erroryarn start
and visit http://localhost:3000
to observe correct behaviorMy initial thought when coming up with this solution, was to rethrow errors from the custom exception filter. Similar approach was tested by @quezak in this issue, but the error shown there still persists.
My understandign from the linked PR is that subclassing BaseExceptionFilter
is the recommended solution for this problem. If I made a mistake somewhere in my implementation, I would appreciate the correction.
Otherwise, I think it would be important to ensure that error handling, achieved through the use of exception filter, can be expressed through the context of E2E tests.
Nest version: 5.3.10
For Tooling issues:
- Node version: 8.11.3
- Platform: Linux
Others:
I have just encountered this issue. I believe it may have something to do with how superagent
sets up the instance, when you do request(app).get('/')...
.
If you extend BaseExceptionFilter
, you need to inject HTTP_SERVER_REF
, but it's an empty object (checked by putting breakpoints in). Hence the error.
constructor(@Inject(HTTP_SERVER_REF) appRef: HttpServer) {
super(appRef); // appRef is {}
}
catch(exception: any, host: ArgumentsHost) {
if (exception instanceof DomainError) {
exception = new HttpException(exception.message, 400);
}
super.catch(exception, host); // this.appRef is {}
}
I don't know what to make of it, but posting just in case it helps anyone.
I worked around the problem by using NestFactory
instead of createTestingModule
:
import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
app = await NestFactory.create(AppModule, {
logger: false,
});
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(400)
.expect(/User not found/);
});
});
Although, I think you lose the benefit of being able to substitute modules.
Should be fixed in 5.4.1, just ensure to entirely remove this:
constructor(@Inject(HTTP_SERVER_REF) appRef: HttpServer) {
super(appRef); // appRef is {}
}
@kamilmysliwiec Confirmed, removing the constructor fixed the problem.
I'm not sure why, but in v5.7.3 I'm trying:
`
\@Catch(NotFoundException)
export class NotFoundExceptionFilter extends BaseExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const res: Response = ctx.getResponse();
const req: Request = ctx.getRequest();
if (req.url.indexOf('/api') == 0)
super.catch(exception, host);
else
res.sendFile(join(__dirname, '../../public/index.html'));
}
}
and applicationRef
still undefined in the BaseExceptionFilter
, causing UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'reply' of undefined
What am I missing?
@nelsonec87 the exception filter has access to the applicationRef
only if the class instance has been instantiated by the framework (new NotFoundExceptionFilter()
will not work).
That was it.
I removed the .useGlobalFilters
and added the Filter to the main module's providers
with APP_FILTER
. It's working, thanks!
Hi! I've started getting this error for some reason on all my E2E tests with Nest 6. It used to work until 2-3 weeks ago. I did not change anything...
This is how my code looks like.
describe('E2E - authorization flows', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
}, 15000);
afterAll(async () => {
await app.close();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
the exception filter has access to the applicationRef only if the class instance has been instantiated by the framework (new NotFoundExceptionFilter() will not work).
But how should one proceed if they want to fallback to the base exception filter inside a global application filter on certain conditions?
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
Hi! I've started getting this error for some reason on all my E2E tests with Nest 6. It used to work until 2-3 weeks ago. I did not change anything...
This is how my code looks like.