Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Сквозные тесты с supertest

Сквозной (end-to-end, e2e) тест проверяет приложение так, как его видит клиент: отправляет настоящие HTTP-запросы и смотрит на коды и тела ответов. В отличие от модульного теста, здесь работают все части сразу — пайпы, гарды, сервисы и база. Проект NestJS уже содержит такой тест и библиотеку supertest для отправки запросов.

Где лежат тесты

Сквозные тесты живут в папке test/ в корне проекта, файлы называются *.e2e-spec.ts. Пример — test/app.e2e-spec.ts. Запускаются они отдельной командой:

npm run test:e2e

Отдельная база для тестов

Тесты создают и удаляют данные, поэтому им нужна своя база. SQLite умеет хранить базу в памяти: если путь равен :memory:, база создаётся при запуске и исчезает после. Переменные окружения важнее файла .env, поэтому достаточно задать DB_PATH в начале теста.

Тест статей

Создайте файл test/posts.e2e-spec.ts:

// test/posts.e2e-spec.ts
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import request from 'supertest';
import { AppModule } from '../src/app.module';
 
process.env.DB_PATH = ':memory:';
 
describe('Posts (e2e)', () => {
  let app: INestApplication;
 
  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();
 
    app = moduleRef.createNestApplication();
    app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
    await app.init();
  });
 
  afterAll(async () => {
    await app.close();
  });
 
  it('GET /posts возвращает пустой список', () => {
    return request(app.getHttpServer()).get('/posts').expect(200).expect([]);
  });
 
  it('GET /posts/999 возвращает 404', () => {
    return request(app.getHttpServer()).get('/posts/999').expect(404);
  });
});

createNestApplication() создаёт приложение из всего AppModule, но без запуска на порту: supertest сам передаёт запросы серверу через app.getHttpServer(). Глобальные настройки из src/main.ts в тест не попадают, поэтому ValidationPipe подключён здесь заново.

Запросы с телом и токеном

// test/posts.e2e-spec.ts
it('POST /posts без токена возвращает 401', () => {
  return request(app.getHttpServer())
    .post('/posts')
    .send({ title: 'Тест', content: 'Текст' })
    .expect(401);
});
 
it('вход и создание статьи', async () => {
  await request(app.getHttpServer())
    .post('/users')
    .send({ email: 'anna@example.com', name: 'Анна', password: 'password123' })
    .expect(201);
 
  const login = await request(app.getHttpServer())
    .post('/auth/login')
    .send({ email: 'anna@example.com', password: 'password123' })
    .expect(200);
 
  const response = await request(app.getHttpServer())
    .post('/posts')
    .set('Authorization', `Bearer ${login.body.accessToken}`)
    .send({ title: 'Тест', content: 'Текст' })
    .expect(201);
 
  expect(response.body.id).toBeDefined();
});

send() отправляет JSON и сам ставит заголовок Content-Type, set() добавляет заголовок, а response.body содержит разобранный ответ. При успехе в терминале появится Tests: 4 passed, 4 total.

Частая ошибка

Тесты запускаются на рабочей базе blog.sqlite и портят или удаляют настоящие данные. Всегда задавайте отдельную базу для тестов до создания модуля: строка process.env.DB_PATH = ':memory:' должна стоять выше Test.createTestingModule(). Для PostgreSQL заведите отдельную тестовую базу и указывайте её в переменных окружения.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.