Skip to content

Database Testing

View raw .md

Magic's database testing utilities run every test against an in-memory SQLite instance, giving you fast and isolated model and query tests without touching the device file system.

On this page

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:

dart
// test/test_helper.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:magic/magic.dart';

Future<void> setupTestDatabase() async {
  await Magic.init(
    envFileName: '.env.testing',
    configFactories: [
      () => testDatabaseConfig,
    ],
  );
}

Map<String, dynamic> get testDatabaseConfig => {
  'database': {
    'default': 'sqlite',
    'connections': {
      'sqlite': {
        'driver': 'sqlite',
        'database': ':memory:',  // In-memory database
      },
    },
  },
};

Running Migrations#

Run migrations before your tests:

dart
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:

dart
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:

dart
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#

dart
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#

dart
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#

dart
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#

dart
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#

dart
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#

dart
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#

dart
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:

dart
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
  });
}
esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.