Build Complete REST API Feature with Nest JS (Using Prisma and Postgresql) from Scratch - Beginner-friendly - PART 6
Tags: nestjs, typescript, testing, node
Published: 2023-10-31 23:36:01

## Scope of discussion:
1. Explain about e2e (end-to-end) testing
2. Handle multiple `env` files
3. Write e2e (end-to-end) testing code

---

In the previous part, we've written code for unit testing. Now, let's move to e2e testing.

What is e2e testing? end-to-end (e2e) testing is a type of software testing that is used to validate the entire system or application from start to finish. E2E testing is designed to simulate real-world scenarios and test the system's functionality, performance, and reliability in a production-like environment.

So, we will hit our real API which will make some updates to the database as well. That's the reason why we need to create `.env.test` and set a different DATABASE_URL in it, especially for e2e testing. We will use a different database. If we don't do that, our production or development database might be broken.

## Pre-configuration

Since we're going to use another `env` file called `.env.test`, we need to rename our `.env` file to something else, for example, I renamed my `.env` to `.env.development`. Why? because `.env` will overwrite other `env` files. I've tried that before. My `.env.test` won't work unless I rename my file `.env`. This will also be useful, we can set any other environment like:
- .env.production
- .env.development
- .env.test
based on our needs. Normally, each environment will have a different configuration.

After that, we need to update our `scripts` inside `package.json` by adding `NODE_ENV` as below:
```json
"start": "NODE_ENV=production nest start",
"start:dev": "NODE_ENV=development nest start --watch",
"start:debug": "NODE_ENV=development nest start --debug --watch",
"start:prod": "NODE_ENV=production node dist/main",
...
"test:e2e": "NODE_ENV=test jest --config ./test/jest-e2e.json"
```

## e2e testing configurations

Unlike unit testing, our e2e testing code is written in a separate folder `/test`. Inside `/test` folder, we'll find two files; `app.e2e-spec.ts` and `jest-e2e.json`. Before diving into the code, let's update `jest-e2e.json` by adding `moduleNameMapper` property:
```json
"moduleNameMapper": {
  "^src/(.*)": "<rootDir>/../src/$1"
}
```
otherwise, we won't be able to import our modules.

Before continuing, let's do some tests by running the e2e test `npm run test:e2e` and see what will happen:
![Error e2e](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hgd9vaha6pjno1eh77mt.png)
We got `404` error because, in part 1, we updated the base endpoint (`/`) to `/test`, so `/` is not found. Let's update it, open up `app.controller.ts`, change `@Get('test')` to `@Get()` and run the `npm run test:e2e` again:
![401 error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xmtgp9wcnwg74aoij1i.png)
We still got an error, but now is different. Instead of getting `404` error, we got `401` error which means that the endpoint is not public. So, let's set this endpoint as public by adding `@Public()` decorator, then run the e2e test again:
![Succes e2e test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9fxdkjedq0jjc0f099v.png) As you can see, now e2e test success ✅

The next step is creating a new file called `.env.test` by copy-paste our `.env` (or `.env.development` in my case) file, then setting the DATABASE_URL to select another database, for example, I select `superb-api-test` database:
![test database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slvg2qd31lze0reif6zy.png)
Then, let's run this command to create tables in our test database `npx dotenv -e .env.test prisma db push`
![Test migrated](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4pgoomppwd5spnnq6ec1.png)

We need to install `config` package from Nest JS:
`$ npm i --save @nestjs/config`

Once the package is installed, we need to register a configuration in our `app.module.ts`. Let's just open `app.module.ts` file and make these updates:
```typescript
imports: [
  // See here. Add config module
  ConfigModule.forRoot({
    envFilePath: process.env.NODE_ENV
      ? `.env.${process.env.NODE_ENV}`
      : '.env',
    isGlobal: true,
  }),

  ...

  UsersModule,
  PostsModule,

  ...
],
```
> Make sure to put ConfigModule at the top of other module imports


The last configuration will be updating `test:e2e` script in our `package.json`. Open up `package.json` and update `test:e2e` script by adding `NODE_ENV=test`. Here is the full snippet:
`"test:e2e": "NODE_ENV=test jest --config ./test/jest-e2e.json"`.

## Now let's write our e2e testing code 🔥

Our e2e testing code will be written in `test/app.e2e-spec.ts`. We'll start with importing some modules; `AppModule`, `UsersModule`, `PostsModule`, `PrismaClient`, and `TRUNCATE` all tables as well before running the tests in `beforeAll`:
```typescript
beforeAll(async () => {
  const moduleFixture: TestingModule = await Test.createTestingModule({
    imports: [AppModule, UsersModule, PostsModule, PrismaClient],
  }).compile();

  app = moduleFixture.createNestApplication();
  await app.init();

  prismaClient = moduleFixture.get<PrismaClient>(PrismaClient);

  await prismaClient.$executeRaw`TRUNCATE "public"."Post" RESTART IDENTITY CASCADE;`;
  await prismaClient.$executeRaw`TRUNCATE "public"."User" RESTART IDENTITY CASCADE;`;
}, 30000);
```

Then, after all tests are completed, we need to close the `app` and disconnect the `prismaClient`:
```typescript
afterAll(async () => {
  await app.close();
  await prismaClient.$disconnect();
}, 30000);
```

Here is the full code:
```typescript
// test/app.e2e-spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
import { UsersModule } from 'src/modules/users/users.module';
import { PostsModule } from 'src/modules/posts/posts.module';
import { PrismaClient } from '@prisma/client';

describe('Superb API (e2e)', () => {
  let app: INestApplication;
  let prismaClient: PrismaClient;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule, UsersModule, PostsModule, PrismaClient],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();

    prismaClient = moduleFixture.get<PrismaClient>(PrismaClient);

    await prismaClient.$executeRaw`TRUNCATE "public"."Post" RESTART IDENTITY CASCADE;`;
    await prismaClient.$executeRaw`TRUNCATE "public"."User" RESTART IDENTITY CASCADE;`;
  }, 30000);

  afterAll(async () => {
    await app.close();
    await prismaClient.$disconnect();
  }, 30000);

  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(200)
      .expect('Hello World!');
  });
});
```

Let's start with `register` and `login`:
```typescript
// register

it('should create a user', async () => {
  const user = {
    email: 'newuser@e2e.test',
    name: 'New User',
    password: '12345678',
  };

  const response = await request(app.getHttpServer())
    .post('/users/register')
    .send(user)
    .expect(201);

  expect(response.body).toEqual({
    id: expect.any(Number),
    email: user.email,
    name: user.name,
  });
});
```


```typescript
it('should login a user', async () => {
  const user = {
    email: 'newuser@e2e.test',
    password: '12345678',
  };

  const response = await request(app.getHttpServer())
    .post('/users/login')
    .send(user)

    .expect(201);

  expect(response.body).toEqual({
    access_token: expect.any(String),
  });
});

it('should not login a user', async () => {
  const user = {
    email: 'wronguser@e2e.test',
    password: '12345678',
  };

  const response = await request(app.getHttpServer())
    .post('/users/login')
    .send(user)

    .expect(404);

  expect(response.body.message).toEqual('User not found');
  expect(response.body.status).toEqual(404);
});
```

Okay, we've written e2e test for `register` and `login`. Now let's try to run it:
![e2e test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fj9du31taaed70qzer8v.png)

All is good. Now you can try to write e2e tests for `Posts` endpoints by yourself 😁

Now we're done with this part :)

The full code of part 6 can be accessed here:
https://github.com/alfism1/nestjs-api/tree/part-six

Moving on to part 7
https://dev.to/alfism1/build-complete-rest-api-feature-with-nest-js-using-prisma-and-postgresql-from-scratch-beginner-friendly-part-7-6me