search ESC

Searching…

No results for "".

Type at least 2 characters to search.

Docs
You are viewing an older version (v1.0.0-alpha.1). Go to the latest.

Database Testing

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:fluttersdk_magic/fluttersdk_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.