I am trying to use dependency injection with get_it package, in order to make unit tests.
I am using these dependencies :
get_it: ^7.6.0
injectable_generator: ^2.1.6
My main function looks like this :
void main(List<String> args) async {
// Configure injection
await setup(Env.test);
await getIt.allReady();
// [...]
}
I wait for the setup with the test environment, and I wait for it to be allReady.
The file uses injection.dart, which contains the setup() function :
GetIt getIt = GetIt.instance;
@InjectableInit(preferRelativeImports: false)
setup(String env) {
getIt.init();
if (env == Env.test) {
getIt.registerLazySingleton<IAuthRepository>(() => TestAuthRepository());
} else {
getIt.registerLazySingleton<IAuthRepository>(() => AuthRepository());
}
}
abstract class Env {
// static const dev = 'dev';
static const prod = 'prod';
static const test = 'test';
}
As you can see, I want to use AuthRepository for production (will call a database), or TestAuthRepository for unit tests (will not call the database).
I declared theses class here, in a file auth_repository.dart :
abstract class IAuthRepository {
Future<int> createNewUser({
required String login,
required String email,
required String password,
});
}
@Injectable(as: IAuthRepository, env: [Env.prod])
class AuthRepository extends BaseRepository implements IAuthRepository {
@override
Future<int> createNewUser({
required String login,
required String email,
required String password,
}) async {
try {
final connection = await getDatabaseConnection();
// ... (make the database call) ...
return 1;
} catch (exception) {
print(exception);
return -1;
}
}
}
@Injectable(as: IAuthRepository, env: [Env.test])
class TestAuthRepository extends BaseRepository implements IAuthRepository {
@override
Future<int> createNewUser({required String login, required String email, required String password}) {
return Future.value(2);
}
}
For now, the testing function createNewUser will only return 2.
I am launching my test here :
void main() {
AuthController authController = AuthController();
test('Registration success', () async {
int response = await authController.register(
login: 'test',
email: '[email protected]',
password: 'Testing193!',
passwordConfirmation: 'Testing193!',
);
expect(response, 1);
});
// ...
}
This test calls for authController.register(), which is here :
Future<int> register({
required String login,
required String email,
required String password,
required String passwordConfirmation,
}) async {
// [...]
// IAuthRepository authRepository = getIt<IAuthRepository>();
IAuthRepository authRepository = getIt.get<IAuthRepository>();
return await authRepository.createNewUser(
login: login,
email: email,
password: password,
);
}
I try to retrieve an IAuthRepository instance with getIt.
However, when I am launching my tests, I get this error :
00:00 +0 -1: Registration success [E]
Bad state: GetIt: Object/factory with type IAuthRepository is not registered inside GetIt.
(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
Did you forget to register it?)
package:get_it/get_it_impl.dart 12:19 throwIfNot
package:get_it/get_it_impl.dart 395:5 _GetItImplementation._findFactoryByNameAndType
package:get_it/get_it_impl.dart 423:29 _GetItImplementation.get
package:toast/controllers/auth_controller.dart 31:44 AuthController.register
test/controllers/auth_controller_test.dart 23:41 main.<fn>
I thought I registered my IAuthRepository class, however it does not work. How can I register it ?
I finally made it work.
Here was the problem : The
setup()function, which registered all the class implementations I needed, was called in themain()function (yes, that IS silly from me...).However, as unit tests are launched specifically to each class, the
main()function was never invoked.So, my test now looks like this :
The
setupGetIt()function is the same assetup()(I renamed it to be more clear), it takes care of registering every class I need.I call it in a
setUpAll()function, in order to register it before all the tests.Note that I also added a
unregisterGetIt()function, which basically only callsgetIt.reset()to unregister every registered class. It is called intearDownAll(), which is called after all tests.I also removed all the
@Injectable()annotation on top of my implemented class, as they are not needed.