Skip to the content
OrielDocs
0.1.0-alpha.10GitHub

Testing

Database testing

Your tests can migrate a database for each test, fill it with factories and seeders, and check the rows your code wrote.

Introduction#

By default, each test runs on a new in-memory SQLite database (see Environment), which is empty: no tables until a test migrates it.

Resetting the database after each test#

RefreshDatabase gives each test a migrated database. Add it to the file's test case:

tests/Feature/Posts.test.ts
import { RefreshDatabase } from '@orieljs/framework/testing'import { test } from 'vitest'import { TestCase } from '../TestCase'const t = TestCase.boot({ uses: [RefreshDatabase] })test('the posts page loads', async () => {  const response = await t.get('/posts')  response.assertOk()})

On the in-memory database, it runs your migrations for each test, on that test's new database. On a database in a file or on a server, it runs migrate:fresh once, and then runs each test inside a transaction that it rolls back afterwards, so no test sees another's rows.

TraitWhat it does
RefreshDatabaseMigrates as above
DatabaseMigrationsRuns migrate:fresh before each test and migrate:rollback after it
DatabaseTransactionsRuns each test in a transaction that is rolled back, without migrating

To use a trait in every test, set it on your TestCase:

tests/TestCase.ts
import type { Application } from '@orieljs/framework'import { RefreshDatabase, TestCase as BaseTestCase } from '@orieljs/framework/testing'import { afterEach, beforeEach } from 'vitest'import { createApplication } from '../bootstrap/app'export class TestCase extends BaseTestCase {  static override hooks = { beforeEach, afterEach }  override readonly uses = [RefreshDatabase]  override createApplication(): Promise<Application> {    return createApplication()  }}

connectionsToTransact lists the connections the transaction traits wrap, the default one unless you name others.

Model factories#

Factories make the rows a test needs:

tests/Feature/Users.test.ts
import { RefreshDatabase } from '@orieljs/framework/testing'import { test } from 'vitest'import { User } from '../../app/Models/User'import { TestCase } from '../TestCase'const t = TestCase.boot({ uses: [RefreshDatabase] })test('models can be instantiated', async () => {  const user = await User.factory().create()  const response = await t.actingAs(user).get('/dashboard')  response.assertOk()})

Oriel finds the factories in database/factories when your tests start.

Running seeders#

t.runSeeder() runs DatabaseSeeder in a test, or the seeder you name:

await t.runSeeder()await t.runSeeder('OrderStatusSeeder')

To seed before every test, set seed, or seeder for another seeder, on your TestCase. RefreshDatabase and DatabaseMigrations then seed after they migrate:

export class TestCase extends BaseTestCase {  override seed = true}

Available assertions#

The test case has these assertions. They read the database, so await each one:

await t.assertDatabaseHas('users', { email: 'sally@example.com' })await t.assertModelExists(user)
AssertionPasses when
assertDatabaseHas(table, data)A row of the table has the values; null matches a null column
assertDatabaseMissing(table, data)No row has them
assertDatabaseCount(table, count)The table has that many rows
assertDatabaseEmpty(table)The table has no rows
assertModelExists(model), assertModelMissing(model)The model's row is in the database, or isn't
assertSoftDeleted(tableOrModel, data?), assertNotSoftDeleted(tableOrModel, data?)The row is soft deleted, or isn't

table is a table's name, or a model, whose table and connection the assertion uses. assertDatabaseHas(), assertDatabaseMissing(), assertDatabaseCount() and assertDatabaseEmpty() take a connection name as their last argument, and assertSoftDeleted() and assertNotSoftDeleted() after the data. A failed assertDatabaseHas() shows a few rows of the table, to help you see what differs.

Privacy policy ·

esc