Тесты с 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 testJapa выведет в терминал список тестов с отметками об успехе и время работы. Запустить только один набор можно командой 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.