Encodera

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

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

Оглавление

Тесты с Japa

Тест — код, который сам проверяет, что приложение работает: открывает страницу, отправляет форму и сравнивает ответ с ожидаемым. После каждой правки достаточно запустить тесты, а не прокликивать сайт руками. В AdonisJS встроен тестовый фреймворк Japa, и проект создаётся с готовой настройкой.

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

tests/
├── bootstrap.ts       настройка плагинов Japa
├── functional/        тесты страниц и API через HTTP
└── unit/              тесты отдельных классов и функций

Наборы тестов (suites) описаны в adonisrc.ts. Функциональные тесты запускают настоящий HTTP-сервер и обращаются к нему, как браузер.

Первый тест

node ace make:test posts --suite=functional

Появится файл tests/functional/posts.spec.ts:

// tests/functional/posts.spec.ts
import { test } from '@japa/runner'
 
test.group('Posts', () => {
  test('показывает список статей', async ({ client }) => {
    const response = await client.get('/posts')
 
    response.assertStatus(200)
    response.assertTextIncludes('Все статьи')
  })
})

test.group объединяет тесты одной темы. Объект client отправляет запросы к приложению, а методы assert... проверяют ответ.

Запуск

node ace test

Japa выведет в терминал список тестов с отметками об успехе и время работы. Запустить только один набор можно командой node ace test functional.

Данные в тестах

Тест должен сам создать нужные данные, а после — убрать их. Для этого каждый тест оборачивают в транзакцию, которая откатывается:

// tests/functional/posts.spec.ts
import { test } from '@japa/runner'
import testUtils from '@adonisjs/core/services/test_utils'
import { PostFactory } from '#database/factories/post_factory'
 
test.group('Posts', (group) => {
  group.each.setup(() => testUtils.db().withGlobalTransaction())
 
  test('показывает статью', async ({ client }) => {
    const post = await PostFactory.merge({ title: 'Тестовая статья' }).create()
 
    const response = await client.get(`/posts/${post.id}`)
    response.assertTextIncludes('Тестовая статья')
  })
})

merge задаёт фабрике конкретные значения. После теста статья исчезнет вместе с откатом транзакции.

Тест от имени пользователя

// tests/functional/posts.spec.ts
import { UserFactory } from '#database/factories/user_factory'
 
test('гость не может открыть форму', async ({ client }) => {
  const response = await client.get('/posts/create').redirects(0)
  response.assertStatus(302)
})
 
test('автор видит форму', async ({ client }) => {
  const user = await UserFactory.create()
  const response = await client.get('/posts/create').loginAs(user)
  response.assertStatus(200)
})

loginAs выполняет запрос от имени пользователя, redirects(0) не даёт клиенту идти по редиректу, чтобы проверить сам ответ 302. UserFactory создаётся так же, как фабрика статей. loginAs работает, когда в tests/bootstrap.ts подключены плагины sessionApiClient и authApiClient.

Тест API

// tests/functional/api_posts.spec.ts
test('API отдаёт статьи', async ({ client, assert }) => {
  const response = await client.get('/api/posts').header('Accept', 'application/json')
  response.assertStatus(200)
  assert.isArray(response.body())
})

response.body() возвращает разобранный JSON, а объект assert из плагина Japa проверяет, что пришёл массив.

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

Тесты запускают на той же базе, что и разработку, и они стирают или засоряют ваши данные. В .env.test укажите отдельную базу — например, другой файл SQLite или базу blog_test. Команда node ace test загружает этот файл поверх .env.

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

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