Creating tests in real database with NestJS, TypeORM and PostgreSQL

gabriel_couto_ecrser

Gabriel Couto

Posted on October 4, 2024

Creating tests in real database with NestJS, TypeORM and PostgreSQL

Intro
Whether you are in a test-driven development mode or not, there are a few useful perks in writing tests:
They provide a safety net that allows developers to confidently make changes, add new features, refactor code knowing that the tests will verify the functionality remains intact.

The pain
Some of us aren't blessed with high-end cpus, and that can't be especially frustating when dealing with huge projects.
Recently i was working in a huge NestJS app, and i just wanted to test some TypeORM Queryes and every time i modified i had to load the entire project. Think of an frustation face, that was me.

Solution
Writing tests is a good pratice, right? what if i could develop faster and indirectly create tests and also avoid the need to create mocks and stubs ? Thats when i thought: Tests agaisnt the real database.

Code

Here is the tests:

import * as dotenv from 'dotenv';
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; 
dotenv.config();
describe('Queryes only test', () => {
    let app: INestApplication;
    let foodRepo: FoodRepository;

    beforeAll(async () => {
        const moduleFixture: TestingModule = await Test.createTestingModule({
            imports: [
                TypeOrmModule.forRootAsync({
                    useFactory: (configService: any) => ({
                        type: 'postgres',

                        host: process.env.TYPEORM_HOST,
                        port: Number(process.env.TYPEORM_PORT),
                        username: process.env.TYPEORM_USERNAME,
                        password: process.env.TYPEORM_PASSWORD,
                        database: process.env.TYPEORM_DATABASE,

                        entities: ['src/app/**/entities/*.entity.*'],
                        synchronize: false,
                        logging: true /* That will make the sql queryes appear in the console */
                    })
                }),
                TypeOrmModule.forFeature([FoodRepository])
            ]
        }).compile();

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

        foodRepo = moduleFixture.get<FoodRepository>(FoodRepository);
    });

    jest.setTimeout(30000);

    it('Must find foods from regular customers', async () => {
        const foodsFromRegularCustomers = await foodRepo.findFoodsFromRegularCustomers().getMany();
        expect(Array.isArray(foodsFromRegularCustomers)).toBeTruthy();
    });

    afterAll(async () => {
        await app.close();
    });
});

Enter fullscreen mode Exit fullscreen mode

and thats my repository:

async findFoodsFromRegularCustomers() {
  const currentDate = new Date();
  const startOfWeek = new Date(currentDate.setDate(currentDate.getDate() - currentDate.getDay()));

  return this.createQueryBuilder('food')
    .innerJoin('food.customer', 'customer')
    .innerJoin('customer.orders', 'orders')
    .select([
      'customer.id', 
      'customer.name', 
      'customer.cpf', 
      'customer.address'
    ])
    .where('orders.createdAt >= :startOfWeek', { startOfWeek })
    .groupBy('customer.id')
    .having('COUNT(orders.id) > :minOrders', { minOrders: 10 })  
}
Enter fullscreen mode Exit fullscreen mode

That test must not be the perfection, but i assure you:
It will help the developer team avoid send broken querys.

If some entity attribute or relationship stop existing, like customer.address the query builder won't break in build time. But the tests will.😼

💖 💪 🙅 🚩
gabriel_couto_ecrser
Gabriel Couto

Posted on October 4, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related