Bad state: Tried to read a provider that threw during the creation of its value

Viewed 163

Problem

I write widget tests for pages. They require BLoC for rendering. get_it is used for dependency injection. The providers use asynchronous functions, so I created a "fake" bloc that returns static data for the widget tests.

For some reason, the bloc injected from get_it doesn't work.

My code

So I have a test:

void main() {
  setUpAll(() async {
    SharedPreferences.setMockInitialValues({});
    await getIt.reset();
    await setup();

getIt.registerFactory(
  () => FakeScheduleBloc( // Stack trace says I have an error in this line
    getSchedule: getIt(),
    getActiveGroup: getIt(),
    getGroups: getIt(),
    setActiveGroup: getIt(),
    getDownloadedSchedules: getIt(),
    deleteSchedule: getIt(),
    getScheduleSettings: getIt(),
    setScheduleSettings: getIt(),
  ),
 );
});

testWidgets('Go to News frome Home', (WidgetTester tester) async {
    await tester.pumpWidget(
        _getFullContext(const HomeNavigatorScreen(isFirstRun: false)));

await tester.tap(find.byKey(WidgetKeys.bottomBarNews));
await tester.pumpAndSettle();

expect(find.byType(NewsScreen), findsOneWidget);
  });
}

A context builder:

Widget _getFullContext(Widget child) {
  return getAdvancedContext(child, [
    BlocProvider<ScheduleBloc>(create: (context) => getIt<FakeScheduleBloc>()), // Stack trace says I have an error in this line
    BlocProvider<HomeNavigatorBloc>(
        create: (context) => getIt<HomeNavigatorBloc>()),
  ]);
}

Widget getAdvancedContext(
    Widget child, List<BlocProviderSingleChildWidget> providers) {
  return MultiBlocProvider(
    providers: providers,
    child: AdaptiveTheme(
      light: lightTheme,
      dark: darkTheme,
      initial: AdaptiveThemeMode.dark,
      builder: (theme, darkTheme) => MaterialApp(
        home: child,
      ),
    ),
  );
}

A fake bloc:

// Fake bloc extends real one, but overrides mapEventToState
class FakeScheduleBloc extends ScheduleBloc {
  FakeScheduleBloc({
/* some variables */
  }) : super(
/* some variables */
);


  @override
  Stream<ScheduleState> mapEventToState(
    ScheduleEvent event,
  ) async* {
    yield SomeStaticData;
  }
}

And a setup:

    final getIt = GetIt.instance;
    
    Future<void> setup() async {
      // BloC / Cubit
      getIt.registerFactory(
        () => ScheduleBloc(
         // There is an implementation of a real ScheduleBloc
        ),
      );
    
      getIt.registerFactory(() => HomeNavigatorBloc());
    }

Errors

When I run tests, I see some errors.

Error while creating FakeScheduleBloc
Stack trace:
 #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39)
...
#464    StackZoneSpecification._registerUnaryCallback.<anonymous closure> (package:stack_trace/src/stack_zone_specification.dart)
<asynchronous suspension>

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building MediaQuery(MediaQueryData(size: Size(800.0, 600.0),
devicePixelRatio: 3.0, textScaleFactor: 1.0, platformBrightness: Brightness.light, padding:
EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, alwaysUse24HourFormat:
false, accessibleNavigation: false, highContrast: false, disableAnimations: false, invertColors:
false, boldText: false, navigationMode: traditional)):
GetIt: The compiler could not infer the type. You have to provide a type and optionally a name. Did
you accidentally do `var sl=GetIt.instance();` instead of var sl=GetIt.instance;
'package:get_it/get_it_impl.dart':
Failed assertion: line 333 pos 7: 'type != null || const Object() is! T'

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following StateError was thrown building MediaQuery(MediaQueryData(size: Size(800.0, 600.0),
devicePixelRatio: 3.0, textScaleFactor: 1.0, platformBrightness: Brightness.light, padding:
EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, alwaysUse24HourFormat:
false, accessibleNavigation: false, highContrast: false, disableAnimations: false, invertColors:
false, boldText: false, navigationMode: traditional)):
Bad state: Tried to read a provider that threw during the creation of its value.
The exception occurred during the creation of type ScheduleBloc.

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following message was thrown:
Multiple exceptions (2) were detected during the running of the current test, and at least one was
unexpected.

My goal

I want to create a fake bloc and inject it in get_it to test widgets, that require bloc.

Why do I use get_it?

I use it because the widgets use BlocBuilder, which means I need to inherit from it to create my fake bloc. ScheduleBloc has a giant constructor that depends on usecases that are also injected in get_it, so I can't get rid of get_it.

0 Answers
Related