Database Testing
- Introduction
- Using In-Memory Database
- Running Migrations
- Using Factories
- Model Testing
- Database Assertions
- Refreshing Database
Introduction
Magic makes it easy to test database interactions using in-memory SQLite databases and model factories. Tests run fast because the database lives entirely in memory and is recreated fresh for each test.
Using In-Memory Database
Configure your tests to use an in-memory database:
// test/test_helper.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:magic/magic.dart';
Future setupTestDatabase() async {
await Magic.init(
envFileName: '.env.testing',
configFactories: [
() => testDatabaseConfig,
],
);
}
Map get testDatabaseConfig => {
'database': {
'default': 'sqlite',
'connections': {
'sqlite': {
'driver': 'sqlite',
'database': ':memory:', // In-memory database
},
},
},
};
Running Migrations
Run migrations before your tests:
void main() {
setUpAll(() async {
await setupTestDatabase();
// Run all migrations
await Migrator().run([
CreateUsersTable(),
CreatePostsTable(),
CreateCommentsTable(),
]);
});
// Your tests here
}
Fresh Database Per Test
For isolation, reset the database for each test:
void main() {
setUp(() async {
await setupTestDatabase();
await Migrator().fresh([ // Drops all tables and re-runs migrations
CreateUsersTable(),
CreatePostsTable(),
]);
});
test('creates user', () async {
// Database is fresh for each test
});
}
Using Factories
Use factories to create test data:
test('creates users with factory', () async {
// Create 10 users
final users = await UserFactory().count(10).create();
expect(users.length, equals(10));
expect(users.first.id, isNotNull);
});
test('creates user with specific attributes', () async {
final admin = (await UserFactory()
.state({'role': 'admin', 'is_active': true})
.create())
.first;
expect(admin.role, equals('admin'));
expect(admin.isActive, isTrue);
});
test('uses factory states', () async {
final unverifiedUsers = await UserFactory()
.unverified()
.count(5)
.create();
for (final user in unverifiedUsers) {
expect(user.emailVerifiedAt, isNull);
}
});
Model Testing
Testing CRUD Operations
group('User Model', () {
test('creates user', () async {
final user = User()
..fill({
'name': 'John Doe',
'email': '[email protected]',
});
await user.save();
expect(user.id, isNotNull);
expect(user.createdAt, isNotNull);
});
test('finds user by id', () async {
final created = (await UserFactory().create()).first;
final found = await User.find(created.id);
expect(found, isNotNull);
expect(found!.id, equals(created.id));
});
test('updates user', () async {
final user = (await UserFactory().create()).first;
user.name = 'Updated Name';
await user.save();
final fresh = await User.find(user.id);
expect(fresh!.name, equals('Updated Name'));
});
test('deletes user', () async {
final user = (await UserFactory().create()).first;
final id = user.id;
await user.delete();
final found = await User.find(id);
expect(found, isNull);
});
});
Testing Relationships
test('user has many posts', () async {
final user = (await UserFactory().create()).first;
await PostFactory()
.state({'user_id': user.id})
.count(3)
.create();
final posts = await user.posts();
expect(posts.length, equals(3));
});
Database Assertions
Assert Record Exists
test('creates record in database', () async {
await UserFactory().state({'email': '[email protected]'}).create();
final exists = await DB.table('users')
.where('email', '[email protected]')
.exists();
expect(exists, isTrue);
});
Assert Record Count
test('seeds correct number of users', () async {
await UserFactory().count(5).create();
final count = await DB.table('users').count();
expect(count, equals(5));
});
Assert Record Deleted
test('deletes user from database', () async {
final user = (await UserFactory().create()).first;
await user.delete();
final exists = await DB.table('users')
.where('id', user.id)
.exists();
expect(exists, isFalse);
});
Assert Specific Values
test('updates user attributes', () async {
final user = (await UserFactory().create()).first;
user.name = 'New Name';
await user.save();
final row = await DB.table('users')
.where('id', user.id)
.first();
expect(row?['name'], equals('New Name'));
});
Refreshing Database
Between Test Groups
void main() {
group('User Tests', () {
setUpAll(() async {
await setupTestDatabase();
await Migrator().run([CreateUsersTable()]);
});
setUp(() async {
// Clear users table before each test
await DB.table('users').delete();
});
test('test 1', () async { /* ... */ });
test('test 2', () async { /* ... */ });
});
}
Using Transactions
Roll back database changes after each test:
void main() {
setUp(() async {
DB.beginTransaction();
});
tearDown(() async {
DB.rollback(); // Undo all changes
});
test('creates user (rolled back)', () async {
await UserFactory().create();
// Changes are rolled back after test
});
}
[!TIP] Use
:memory:database and transactions for the fastest test execution. Each test runs in isolation without persisting data.