fluttercommunity / get_it

Get It - Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App. Maintainer: @escamoteur
https://pub.dev/packages/get_it
MIT License
1.35k stars 148 forks source link

[Feature] Option for ignoreReassignment #335

Closed dmrickey closed 7 months ago

dmrickey commented 1 year ago

Right now GetIt errors if we try to re-register a type. Optionally, there's an option to allow re-registration, but that will replace the first registered type with the second.

I'm looking for a third option that will just ignore any attempts to re-register an already registered type (hopefully silently).

This would be used exclusively for testing. Right now if we need to register a singleton that has side effects or real dependencies as a side effect of being created, there's not a good way to get around that in testing.

Contrived simple example: I've got class RealFoo extends IFoo that reaches out to a real database as part of being instantiated. I register this class a dependency during startup as part of my app's construction as GetIt.I.registerSingleton<IFoo>(RealFoo()). And my test calls app().startup() and then verifies that various things happened during startup.

I'd instead like to do something like this with my tests

setup() {
  GetIt.I.ignoreReassignment = true;
}
test('the test', () {
  GetIt.I.registerSingleton<IFoo>(FakeFoo());

  // RealFoo attempts to be registered in here, but is now ignored as GetIt already has a registered `IFoo`
  app().startup(); 

  // verify stuff
}

This would make it so that when RealFoo is registered as part of creating App, it just gets ignored as I already registered FakeFoo and the test can then verify the other stuff that I actually care about.

escamoteur commented 1 year ago

why not just registering in setup?

feinstein commented 11 months ago

Also ignoreReassignment could be marked as @visibleForTesting.

why not just registering in setup?

I have a setup function the runs at the beginning, but I want to reassign one of the singletons being created there. The only way I have is verbose, I need to pass nullable instances to setup, so I can verify if they are null or not. If they are not null, I use them, so this is how we can inject fake instances for tests. This very verbose and could be immensely simplified if we had a ignoreReassignmentForTesting.

Here's an example of what I have to do now:

@override
  Future<void> setup({
    Dio? dio,
    ExampleRestDataSource? exampleRestDataSource,
    PackageInfo? packageInfo,
    ExampleRepository? ExampleRepository,
    ExampleCubit? ExampleCubit,
  }) async {
    getIt
      ..registerSingleton(dio ?? Dio())
      ..registerSingleton(exampleRestDataSource ?? ExampleRestDataSource(getIt()))
      ..registerSingleton(packageInfo ?? await PackageInfo.fromPlatform())
      ..registerSingleton(exampleRepository ??
          ExampleRepository(
            exampleRestDataSource: getIt(),
            packageInfo: getIt(),
          ))
      ..registerSingleton(exampleCubit ?? ExampleCubit(exampleRepository: getIt()));
  }
dmrickey commented 10 months ago

why not just registering in setup?

Because that yields the same result as what I outlined above. If I moveGetIt.I.registerSingleton<IFoo>(FakeFoo()); into setup, the test is still going to blow up when I call app.startup because startup registers RealFoo. Right now we have to have suboptimal code in our production code so that we can conditionally register dependencies based on whether we're testing or not.

escamoteur commented 7 months ago

OK, I think I finally understood this issue the first time, my bad. We probably should call the option something like skipDoubleRegistrations

escamoteur commented 7 months ago

Implemented in V7.6.8