fluttertestinginjectable

Mocking an injectable class for testing that other regular clases depend on


I have an abstract class DatabaseManager, and an implementation DatabaseManagerImpl, the latter is annotated like this:

@LazySingleton(as: DatabaseManager)
class DatabaseManagerImpl implements DatabaseManager {

A lot of other classes then depend on DatabaseManager like this:

@LazySingleton()
class SomeClass {
  const SomeClass(this._databaseManager);

  final DatabaseManager _databaseManager;

I'm trying to add integration testing - as in, testing multiple modules at the same time, not the one where you run the app on a device. Basically, I'm testing how the backend executes certain features. Most of the backend classes depend on DatabaseManager (it initializes and provides the database connection).

First of all, I want to initialize an in-memory database for testing and fill it with fake values, so I need to mock DatabaseManager. The second reason is that I need to use sqflite_ffi because the tests are run on Linux.

The problem is, I don't know where to put DatabaseManagerMock: under the lib or test folder?

  1. if I put it under the test folder (which is where it belongs ideally), I can't generate injectable config for it because it (obviously) doesn't get included into the generated file under lib. I had an idea to generate a second injectable config file for tests and then run them in this order:
await configureDependencies() // from the main project
await configureTestDependencies() // from tests

But injectable breaks when I do that: it can't generate proper import paths for the test directory.

  1. Putting DatabaseManagerMock under the lib folder. Since I need sqflite_ffi only for testing, it should be a dev dependency, but that gives a depend_on_referenced_packages warning when I import it to use in DatabaseManagerMock.

What is the proper way to do this? I think I might be overthinking and there is a completely different approach to this that I'm missing.


Solution

  • I fixed the problem with the generated imports for injectable and the PR was merged yesterday, so now it's possible to do this:

    Create a second injectable initializer under the test directory:

    // test/injectable/configure_test_dependencies.dart
    import 'package:get_it/get_it.dart';
    import 'package:injectable/injectable.dart';
    
    import 'configure_test_dependencies.config.dart';
    
    const testEnv = 'test';
    final getIt = GetIt.instance;
    
    @InjectableInit(
      initializerName: 'testInit',
      preferRelativeImports: true,
      generateForDir: ['test', 'lib'],
    )
    Future<GetIt> configureTestDependencies() {
      return getIt.testInit(environment: testEnv);
    }
    
    

    Then create the mock classes under test/ and set their environment to testEnv. Their corresponding non-mock classes (in lib/) must be within some other environment, like mainEnv. This is not important for the classes that are not mocked. Example:

    // test/mocks/database_manager_mock.dart
    @LazySingleton(as: DatabaseManager, env: [testEnv])
    class DatabaseManagerMock extends DatabaseManager {
    

    Then configureTestDependencies() generates get_it initializer that includes all the annotated classes from lib/ plus the new mock classes.