modular icon indicating copy to clipboard operation
modular copied to clipboard

Bug: Recover injection: IGetCurrentUserUseCase<Exception>

Open Bwolfs2 opened this issue 1 year ago • 6 comments

Describe the bug I am migrating from Modular 5.0 to Modular 6.0 and I'm encountering the following problem: I need to retrieve a composite class that can be injected according to its definition. Here's a test example of my use case. The injection is not being found.

To Reproduce

import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:flutter_test/flutter_test.dart';

abstract class IGetCurrentUserUseCase<T> {
  T call();
}

class GetCurrentUserUseCase implements IGetCurrentUserUseCase<Exception> {
  @override
  Exception call() {
    throw UnimplementedError();
  }
}

abstract class IListenCurrentUserChangesUseCase {
  Stream<String> call();
}

class ListenCurrentUserChangesUseCase
    implements IListenCurrentUserChangesUseCase {
  @override
  Stream<String> call() {
    return Stream.fromIterable(['Bwolf', 'Deivao', 'Jacob']);
  }
}

class DrawerViewModel {
  final IGetCurrentUserUseCase<Exception> _getCurrentUserUsecase;
  final IListenCurrentUserChangesUseCase _listenCurrentUserChangesUsecase;

  const DrawerViewModel(
    this._getCurrentUserUsecase,
    this._listenCurrentUserChangesUsecase,
  );

  Exception get currentUser => _getCurrentUserUsecase();
  Stream<String> get currentUserStream => _listenCurrentUserChangesUsecase();
}

void main() {
  testWidgets('Test Inner Inject', (tester) async {
    final modularApp = ModularApp(
      module: CustomModule(),
      child: const AppWidget(),
    );

    await tester.pumpWidget(modularApp);

    await tester.pump();
    expect(Modular.get<IGetCurrentUserUseCase<Exception>>(), isNotNull);
    expect(Modular.get<IListenCurrentUserChangesUseCase>(), isNotNull);
    expect(Modular.get<DrawerViewModel>(), isNotNull);
  });
}

///MOCKS
class CustomModule extends Module {
  @override
  void binds(Injector i) {
    i.addLazySingleton<IGetCurrentUserUseCase>(GetCurrentUserUseCase.new);
    i.addLazySingleton<IListenCurrentUserChangesUseCase>(
      ListenCurrentUserChangesUseCase.new,
    );

    i.addInstance<DrawerViewModel>(DrawerViewModel(
      i.get<IGetCurrentUserUseCase<Exception>>(),
      i.get<IListenCurrentUserChangesUseCase>(),
    ));
  }

  @override
  void routes(RouteManager r) {
    r.child('/', child: (__) => Container());
  }
}

class AppWidget extends StatelessWidget {
  const AppWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routeInformationParser: Modular.routeInformationParser,
      routerDelegate: Modular.routerDelegate,
    );
  }
}

Screenshots image

Bwolfs2 avatar Aug 08 '23 20:08 Bwolfs2

Generic Type diff. IGetCurrentUserUseCase<Exception> != IGetCurrentUserUseCase

In Modular 6 the Type work as Key and must be a same aways.

jacobaraujo7 avatar Aug 09 '23 18:08 jacobaraujo7

Hi @jacobaraujo7 even if we specify the generic type the issue still persist. The same problem occurs with exported bindings.

BenevidesLecontes avatar Aug 09 '23 23:08 BenevidesLecontes

@jacobaraujo7 the usecase with exported bindings

import 'package:flutter_modular/flutter_modular.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

abstract class IGetCurrentUserUseCase<T> {
  T call();
}

class GetCurrentUserUseCase implements IGetCurrentUserUseCase<String> {
  final IAuthService _authService;

  GetCurrentUserUseCase(this._authService);

  @override
  String call() {
    return _authService.getUser();
  }
}

abstract class IListenCurrentUserChangesUseCase {
  Stream<String> call();
}

class ListenCurrentUserChangesUseCase
    implements IListenCurrentUserChangesUseCase {
  @override
  Stream<String> call() {
    return Stream.fromIterable(['Bwolf', 'Deivao', 'Jacob']);
  }
}

abstract interface class IAuthService {
  String getUser();
}

class AuthService implements IAuthService {
  @override
  String getUser() => 'Teste';
}

class DrawerViewModel {
  final IGetCurrentUserUseCase<String> _getCurrentUserUsecase;
  final IListenCurrentUserChangesUseCase _listenCurrentUserChangesUsecase;

  const DrawerViewModel(
    this._getCurrentUserUsecase,
    this._listenCurrentUserChangesUsecase,
  );

  String get currentUser => _getCurrentUserUsecase();

  Stream<String> get currentUserStream => _listenCurrentUserChangesUsecase();
}

void main() {
  testWidgets('Test Inner Inject', (tester) async {
    final modularApp = ModularApp(
      module: CustomModule(),
      child: const AppWidget(),
    );

    await tester.pumpWidget(modularApp);

    await tester.pump();
    expect(Modular.get<IGetCurrentUserUseCase<String>>(), isNotNull);
    expect(Modular.get<IListenCurrentUserChangesUseCase>(), isNotNull);
    expect(Modular.get<DrawerViewModel>(), isNotNull);
  });
}

///MOCKS
class CustomModuleWithExports extends Module {
  @override
  void exportedBinds(Injector i) {
    i.addLazySingleton<IAuthService>(AuthService.new);
  }
}

///MOCKS
class CustomModule extends Module {
  @override
  List<Module> get imports => [
        CustomModuleWithExports(),
      ];

  @override
  void binds(Injector i) {
    i.addLazySingleton<IGetCurrentUserUseCase<String>>(
        GetCurrentUserUseCase.new);
    i.addLazySingleton<IListenCurrentUserChangesUseCase>(
      ListenCurrentUserChangesUseCase.new,
    );

    i.addInstance<DrawerViewModel>(
      DrawerViewModel(
        i.get<IGetCurrentUserUseCase<String>>(),
        i.get<IListenCurrentUserChangesUseCase>(),
      ),
    );
  }

  @override
  void routes(RouteManager r) {
    r.child('/', child: (__) => Container());
  }
}

class AppWidget extends StatelessWidget {
  const AppWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routeInformationParser: Modular.routeInformationParser,
      routerDelegate: Modular.routerDelegate,
    );
  }
}

CleanShot 2023-08-10 at 06 08 35@2x

BenevidesLecontes avatar Aug 10 '23 09:08 BenevidesLecontes

My test Works with generic image

jacobaraujo7 avatar Aug 10 '23 16:08 jacobaraujo7

@jacobaraujo7 with generics indeed worked and now we're stuck with the exportedBindings issue.

BenevidesLecontes avatar Aug 10 '23 21:08 BenevidesLecontes

@BenevidesLecontes https://github.com/Flutterando/modular/pull/884

Bwolfs2 avatar Aug 24 '23 23:08 Bwolfs2