'Testing interceptors in nest.js won't wait for the previous test to finish with mocha as test runner
So I've written a LoggingInterceptor for a Nest.js project.
This is my implementation
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
constructor(private readonly logger: LoggingService) {}
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> {
// Timestamp from start of request so we can calculate duration
const reqStartTime = getNanoSecTime()
const httpContext = context.switchToHttp()
const request = httpContext.getRequest<Request>()
return next.handle().pipe(
// If the response is successful, we'll log the HTTP response
map((data) => {
const response = httpContext.getResponse<Response>()
const logData = LoggingService.getLogData(
reqStartTime,
request,
response.statusCode,
)
this.logger.log('HTTP response completed', logData)
return data
}),
// If there is an error in the handler, we'll log the
// HTTP response with additional error metadata
catchError((err) => {
let statusCode: number
if (err instanceof HttpException) {
statusCode = err.getStatus()
} else {
statusCode = 500
}
console.log('statusCode', statusCode)
const logData = LoggingService.getLogData(
reqStartTime,
request,
statusCode,
)
this.logger.error('Error in handler', err, logData)
// We'll rethrow the error to be caught by Nest's built-in Exception Filter
throw err
}),
)
}
}
And this is my test setup
const mockLoggingService = sinon.createStubInstance(LoggingService)
const getLogDataStub = sinon
.stub(LoggingService, 'getLogData')
.returns(mockLogData)
const mockContext = {
switchToHttp: () => ({
getRequest: () => mockRequest,
getResponse: () => mockResponse,
}),
} as ExecutionContext
describe('LoggingInterceptor', () => {
let interceptor: LoggingInterceptor
beforeEach(() => {
interceptor = new LoggingInterceptor(mockLoggingService)
})
afterEach(() => sinon.restore())
it('call LoggingService.log with correct arguments', (done) => {
const mockHandler: CallHandler = {
handle: () => of([]),
}
interceptor.intercept(mockContext, mockHandler).subscribe({
next: () => {
expect(getLogDataStub.calledOnce).to.be.true
expect(mockLoggingService.log.calledOnce).to.be.true
expect(mockLoggingService.log.args[0][0]).to.equal(
'HTTP response completed',
)
expect(mockLoggingService.log.args[0][1]).to.equal(mockLogData)
},
complete: () => {
done()
},
})
})
it('should call LoggingService.error and handle when err is instance of HttpException', (done) => {
const error = new HttpException('Bad Gateway', 502)
const mockHandler: CallHandler = {
handle: () => throwError(() => error),
}
interceptor.intercept(mockContext, mockHandler).subscribe({
error: () => {
expect(getLogDataStub.calledOnce).to.be.true
expect(getLogDataStub.args[0][2]).to.equal(502)
expect(mockLoggingService.error.calledOnce).to.be.true
expect(mockLoggingService.error.args[0][0]).to.equal('Error in handler')
expect(mockLoggingService.error.args[0][1]).to.equal(error)
expect(mockLoggingService.error.args[0][2]).to.equal(mockLogData)
done()
},
})
})
I'm pretty new to Observables, so I'm really not sure why my test suite keeps on failing. Every test passes if run individually, but there is evidently some sort of memory leak occurring when running all tests, as the statusCode from the first test leaks into the statusCode for the 2nd and 3rd tests, always causing my assertions for the last tests to fail.
Solution 1:[1]
Well, it turns out the problem with my test had nothing to do with Observables and everything to do with where I was initializing my stubs.
If anyone's curious, I had to move my mockLoggingService into a beforeEachHook to make sure a new instance was being created before each test.
describe('LoggingInterceptor', () => {
let interceptor: LoggingInterceptor
let mockLoggingService
// originally, my mockLoggingService was being declared outside my test block
beforeEach(() => {
mockLoggingService = sinon.createStubInstance(LoggingService)
interceptor = new LoggingInterceptor(mockLoggingService)
})
//
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Seanyboy Lee |
