I have two screens (log in and sign up) which have buttons linking to each other respectively. For the log in page a button triggers pushReplacementNamed
to navigate to the sign up page. Similarly in the sign up page a different button triggers pushReplacementNamed
to navigate to the log in page.
login_screen.dart:
SecondaryButton(
onPressed: () {
Navigator.pushReplacementNamed(context, SignupScreen.id);
},
key: LoginScreen.signupBtnKey,
child: Text('Sign Up'),
),
signup_screen.dart:
SecondaryButton(
onPressed: () {
Navigator.pushReplacementNamed(context, LoginScreen.id);
},
key: SignupScreen.loginBtnKey,
child: Text('Log In'),
)
When testing on the emulator both buttons seem to work fine but when trying to run automated tests it doesn't work for the sign up screen.
login_screen_test.dart
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
void main() {
group('Login Screen Widget Tests', () {
NavigatorObserver mockObserver;
setUp(() {
mockObserver = MockNavigatorObserver();
});
Widget createLoginScreen() {
return MaterialApp(
initialRoute: LoginScreen.id,
routes: {
LoginScreen.id: (context) => LoginScreen(),
SignupScreen.id: (context) => SignupScreen(),
},
navigatorObservers: [mockObserver],
);
}
testWidgets(
'Testing if sign up button shows up and triggers navigation after tapped',
(tester) async {
await tester.pumpWidget(createLoginScreen());
expect(find.byKey(LoginScreen.signupBtnKey), findsOneWidget);
await tester.tap(find.byKey(LoginScreen.signupBtnKey));
await tester.pumpAndSettle();
verify(
mockObserver.didReplace(
oldRoute: anyNamed('oldRoute'),
newRoute: anyNamed('newRoute'),
),
);
expect(find.byType(SignupScreen), findsOneWidget);
expect(find.byType(LoginScreen), findsNothing);
},
);
});
}
signup_screen_test.dart
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
void main() {
group('Signup Screen Widget Tests', () {
NavigatorObserver mockObserver;
setUp(() {
mockObserver = MockNavigatorObserver();
});
Widget createSignupScreen() {
return MaterialApp(
initialRoute: SignupScreen.id,
routes: {
LoginScreen.id: (context) => LoginScreen(),
SignupScreen.id: (context) => SignupScreen(),
},
navigatorObservers: [mockObserver],
);
}
testWidgets(
'Testing if log in button shows up and triggers navigation after tapped',
(tester) async {
await tester.pumpWidget(createSignupScreen());
expect(find.byKey(SignupScreen.loginBtnKey), findsOneWidget);
await tester.tap(find.byKey(SignupScreen.loginBtnKey));
await tester.pumpAndSettle();
verify(
mockObserver.didReplace(
oldRoute: anyNamed('oldRoute'),
newRoute: anyNamed('newRoute'),
),
);
expect(find.byType(LoginScreen), findsOneWidget);
expect(find.byType(SignupScreen), findsNothing);
},
);
});
}
This fails with the following exception:
The following TestFailure object was thrown running a test:
No matching calls. All calls: MockNavigatorObserver.navigator,
MockNavigatorObserver._navigator==NavigatorState#03a3a(tickers: tracking 1 ticker),
MockNavigatorObserver.didPush(MaterialPageRoute<dynamic>(RouteSettings("/signup", null), animation:
AnimationController#9b7dc(⏭ 1.000; paused; for MaterialPageRoute<dynamic>(/signup))), null)
Which suggests that the didReplace() method isn't being triggered. The code is almost identical on each page and so I can't understand why one would pass but the other one fail.
Would appreciate any insight, might be something obvious!
Additional text field earlier in the page was bumping it off screen. Needed to add a tester.fling()
.