Flutter: How to unit testing moor/drift?

738 Views Asked by At

I already implementation of Drift for local storage, and want make it testable function. But I get stack and idk how to fix it the unit test.

HomeDao

@DriftAccessor(tables: [RepositoriesTable])
class HomeDao extends DatabaseAccessor<AppDatabase> with _$HomeDaoMixin {
  HomeDao(AppDatabase db) : super(db);

  Future<List<RepositoriesTableData>> getRepositories() async =>
      await select(repositoriesTable).get();
}

AppDatabase

@DriftDatabase(
  tables: [RepositoriesTable],
  daos: [HomeDao],
)
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_openConnection());

  @override
  int get schemaVersion => 1;
}

QueryExecutor _openConnection() {
  return SqfliteQueryExecutor.inDatabaseFolder(
    path: 'db.sqlite',
    logStatements: true,
  );
}

LocalDataSources

abstract class GTHomeLocalDataSource {
  const GTHomeLocalDataSource();

  Future<List<RepositoriesTableData>> getRepositories();
}

class GTHomeLocalDataSourceImpl implements GTHomeLocalDataSource {
  final AppDatabase appDatabase;

  const GTHomeLocalDataSourceImpl({required this.appDatabase});

  @override
  Future<List<RepositoriesTableData>> getRepositories() async =>
      await appDatabase.homeDao.getRepositories();
}

UnitTesting

void main() => testGTHomeLocalDataSource();

class MockDatabaseHandler extends Mock implements AppDatabase {}

void testGTHomeLocalDataSource() {
  late GTHomeLocalDataSource localDataSource;
  late AppDatabase databaseHandler;

  setUp(() {
    databaseHandler = MockDatabaseHandler();
    localDataSource = GTHomeLocalDataSourceImpl(
      appDatabase: databaseHandler,
    );
  });

  group("GTHomeLocalDataSource -", () {
    test(''' \t
      GIVEN Nothing
      WHEN call getRepositories
      THEN databaseHandler select function has been called and return list of RepositoriesTableData
      ''', () async {
      // GIVEN
      when(() => databaseHandler.homeDao.getRepositories())
          .thenAnswer((_) => Future.value(repositoriesDummyTable));

      // WHEN
      final result = await localDataSource.getRepositories();

      // THEN
      verify(() => databaseHandler.homeDao.getRepositories());

      expect(result, isA<List<RepositoriesTableData>>());
      expect(result.length, repositoriesDummyTable.length);
      expect(result.first.language, repositoriesDummyTable.first.language);
    });
  });

  tearDown(() async {
    await databaseHandler.close();
  });
}

My function is work well for get data from the local db and show it in the app, but when running as unit test, I stacked with this error.

package:gt_core/local/database/database_module.g.dart 424:22           MockDatabaseHandler.homeDao
package:gt_home/data/data_sources/gt_home_local_datasource.dart 20:25  GTHomeLocalDataSourceImpl.getRepositories
test/data/data_sources/gt_home_local_datasource_test.dart 35:44        testGTHomeLocalDataSource.<fn>.<fn>
test/data/data_sources/gt_home_local_datasource_test.dart 29:12        testGTHomeLocalDataSource.<fn>.<fn>
type 'Null' is not a subtype of type 'Future<void>'
package:drift/src/runtime/api/db_base.dart 125:16                MockDatabaseHandler.close
test/data/data_sources/gt_home_local_datasource_test.dart 47:27  testGTHomeLocalDataSource.<fn>
test/data/data_sources/gt_home_local_datasource_test.dart 46:12  testGTHomeLocalDataSource.<fn>
===== asynchronous gap ===========================
dart:async                                                       _completeOnAsyncError
test/data/data_sources/gt_home_local_datasource_test.dart        testGTHomeLocalDataSource.<fn>
test/data/data_sources/gt_home_local_datasource_test.dart 46:12  testGTHomeLocalDataSource.<fn>

type 'Future<List<RepositoriesTableData>>' is not a subtype of type 'HomeDao'

Anyone know how to fix it?

1

There are 1 best solutions below

0
Alexander Dischberg On

If you use Mocking to test Drift database, then you'll need to mock the method call as well, otherwiser the method will return null which is the default behavior for Mockito. For example.

// return rowid
when(db.insertItem(any)).thenAnswer((_) => 1);

However it is recommended as per Drift documentation to use in-memory sqlite database which doesn't require real device or simulator for testing.

This issue was also has been discussed here

Using in memory database

import 'package:drift/native.dart';
import 'package:test/test.dart';

import 'package:my_app/src/database.dart'; 

void main() {
  MyDatabase database;
  MyRepo repo;

  setUp(() {
    database = MyDatabase(NativeDatabase.memory());
    repo = MyRepo(appDatabase: database);
  });

  tearDown(() async {
    await database.close();
  });

  group('mytest', () {
    test('test create', () async {
      await repo.create(MyDateCompanion(title: 'some name'));

      final list = await repo.getItemList();
      expect(list, isA<MyDataObject>())
    })
  });
}