Unable to test navigation using GoRouter in my Flutter app

Scenario

I am new to widget testing and GoRouter. I have a very simple test case:

  • There are two screens – LoginPage which loads as the first screen, and a CheckoutPage
  • LoginPage has a button. Once I tap on it, I’m being taken to the CheckoutPage.

I am using GoRouter for my navigation.

Code

Here is how the MaterialApp is set up in main.dart:

MaterialApp.router(
            debugShowCheckedModeBanner: false,
            routerConfig: GoRouteInformation.router,
            title: "Neon Student",
          ),

Here are the routerConfig and RouteNames classes:

RouterConfig

class GoRouteInformation {
  static final GoRouter router = GoRouter(
    routes: [
      GoRoute(
        path: "https://stackoverflow.com/",
        name: RouteNames.login,
        builder: (context, state) => const LoginPage(),
        routes: [
          GoRoute(
            path: 'checkout',
            name: RouteNames.checkout,
            builder: (context, state) => const CheckoutPage(),
          ),
        ],
      ),
    ],
  );
}

RouteNames

class RouteNames {
  static const String login = 'login';
  static const String checkout="checkout";
}

The following is the code in loginpage_test.dart:

testWidgets('Navigates from LoginPage to CheckoutPage', (WidgetTester tester) async {
    await tester.pumpWidget(const MainApp());
    await tester.pumpWidget(const MaterialApp(home: LoginPage()));

    expect(find.byType(LoginSection), findsOneWidget);
    expect(find.byType(SplitDivider), findsOneWidget);

    Finder subscribeButton = find.byKey(const ValueKey('Subscribe'));
    expect(subscribeButton, findsOneWidget);

    await tester.tap(subscribeButton);
    await tester.pumpAndSettle();

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

EDIT: Here is my main.dart

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ScreenUtilInit(
      designSize: const Size(428, 926),
      minTextAdapt: true,
      splitScreenMode: true,
      builder: (_, child) {
        return GestureDetector(
          onTap: () {
            FocusManager.instance.primaryFocus?.unfocus();
          },
          behavior: HitTestBehavior.opaque,
          child: MaterialApp.router(
            debugShowCheckedModeBanner: false,
            routerConfig: GoRouteInformation.router,
            title: "Neon Student",
          ),
        );
      },
    );
  }
}

Problem

When I run flutter test, I get the following error:

  1. The following assertion was thrown while handling a gesture:
    No GoRouter found in context
    ‘package:go_router/src/router.dart’:
    Failed assertion: line 507 pos 12: ‘inherited != null’
  2. The following TestFailure was thrown running a test:
    Expected: exactly one matching candidate
    Actual: _TypeWidgetFinder:<Found 0 widgets with type “CheckoutPage”: []>
    Which: means none were found but one was expected

Request

I would like to know what is wrong in my widget test code which is causing the test to fail and how to rectify it.

  • Could you share the full code of your main file?

    – 

  • @AlvaroDosio I have edited my post to add main.dart contents

    – 

await tester.pumpWidget(const MainApp());
await tester.pumpWidget(const MaterialApp(home: LoginPage()));

This is the problematic part. It has two problems:

  1. This first pump is basically ignored, because you pump another widget. You can have only one widget present in the “sandbox” at a given moment.
  2. You pump a bare-boned MaterialApp, without the routing.

To have the router available in your tests, you must pass it to the pumped app just like you do in MainApp:

await tester.pumpWidget(
  MaterialApp.router(
    routerConfig: GoRouteInformation.router,
    home: const LoginPage(),
  ),
);

You can make a utility widget TestApp in your test/ directory that would be reused in your widget tests that will have all the surroundings needed for the widgets to function properly.

This explains the error you are getting:

The following assertion was thrown while handling a gesture: No GoRouter found in context ‘package:go_router/src/router.dart’: Failed assertion: line 507 pos 12: ‘inherited != null’

It means that GoRouter.of(context) couldn’t find the router in the widget tree above your LoginPage. This is because your MaterialApp from the pump didn’t have that router.

Leave a Comment