Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
NestJS Integration/E2E Testing Example with TypeORM, Postgres, JWT
import { Test, TestingModule } from '@nestjs/testing'
import { INestApplication, LoggerService } from '@nestjs/common'
import * as request from 'supertest'
import { AppModule } from './../src/app.module'
class TestLogger implements LoggerService {
log(message: string) {}
error(message: string, trace: string) {}
warn(message: string) {}
debug(message: string) {}
verbose(message: string) {}
}
describe('AppController (e2e)', () => {
let app: INestApplication
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile()
app = moduleFixture.createNestApplication()
app.useLogger(new TestLogger())
await app.init()
// tip: access the database connection via
// const connection = app.get(Connection)
// const a = connection.manager
})
afterAll(async () => {
await Promise.all([
app.close(),
])
})
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!')
})
describe('Authentication', () => {
let jwtToken: string
describe('AuthModule', () => {
// assume test data includes user test@example.com with password 'password'
it('authenticates user with valid credentials and provides a jwt token', async () => {
const response = await request(app.getHttpServer())
.post('/auth/login')
.send({ email: 'test@example.com', password: 'password' })
.expect(200)
// set jwt token for use in subsequent tests
jwtToken = response.body.accessToken
expect(jwtToken).toMatch(/^[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$/) // jwt regex
})
it('fails to authenticate user with an incorrect password', async () => {
const response = await request(app.getHttpServer())
.post('/auth/login')
.send({ email: 'test@example.com', password: 'wrong' })
.expect(401)
expect(response.body.accessToken).not.toBeDefined()
})
// assume test data does not include a nobody@example.com user
it('fails to authenticate user that does not exist', async () => {
const response = await request(app.getHttpServer())
.post('/auth/login')
.send({ email: 'nobody@example.com', password: 'test' })
.expect(401)
expect(response.body.accessToken).not.toBeDefined()
})
})
describe('Protected', () => {
it('gets protected resource with jwt authenticated request', async () => {
const response = await request(app.getHttpServer())
.get('/protected')
.set('Authorization', `Bearer ${jwtToken}`)
.expect(200)
const data = response.body.data
// add assertions that reflect your test data
// expect(data).toHaveLength(3)
})
})
})
})
@damiankim1
Copy link

damiankim1 commented Jun 2, 2022

Thanks for the code snippet, however It think this is more of an integration test rather than e2e test: per test definition by MSFT:
https://microsoft.github.io/code-with-engineering-playbook/automated-testing/e2e-testing/testing-comparison/

For e2e test, I would expect to hit API with how real client does, and test against entire response data + json response along with status code. One way to test each endpoint would be utilizing tools like stoplight.io.

@firxworx
Copy link
Author

firxworx commented Jun 2, 2022

@damiankim1 that's a great link from MSFT + you are quite right that the specific example is best described as an integration test. I did name the gist "Integration/E2E" with "Integration" placed first :-)

Why call it both? FYI you can use the supertest library (w/ underlying superagent) to hit any URL you want, so the gist could feasibly serve as boilerplate for both test strategies especially if testing an API. The library even supports unix domain sockets. This is a gist after all - an aide-mémoire and time-saving starter :)

I agree a strong E2E testing should flex as much of the stack as it can because there's a lot that can go wrong in between a user and an API that passes tests at the app-focused level of integration tests, especially in the cloud-driven IaC and CI/CD automate everything kind of Internet we live in these days with tons of code + configuration in between.

Cypress (https://www.cypress.io/) is my pick for broader e2e + front-end testing these days.

I can't help but notice your recent account... Are you like the face of some targeted guerilla content marketing for stoplight.io!? If you work there, I'm curious -- why not use your real persona? It'd be even more authentic + legit + personal!

@damiankim1
Copy link

damiankim1 commented Jun 2, 2022

@firxworx Haha, sorry it is a new github account from the company I just joined a few days ago. I was looking into configuring integration test on existing project, but I am new to nestjs so I came across your blog post. cypress is also a good tool I agree, my previous company used stoplight for API e2e test. While I agree supertest is a very handy tool, I still think it is more suitable for integration test. Again, thank you for the code example, will help me a lot :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment