I'm trying to build a Widget test for a screen that's using the Provider framework.
The app has 1 screen with 1 button, when I tap the button it will trigger a function to update the state. Once the state is updated a string with the key Key('LoadedString')
will appear.
When manually testing in the simulator this works as expected.
When I'm trying to use a widget test to verify the expected behavior it seems like the UI is not updating.
This is the Widget:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider_page_state_controller.dart';
class ProviderPageWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(providers: [
ChangeNotifierProvider<ProviderPageStateController>(
create: (context) => ProviderPageStateController())
], child: HomePage());
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
var screenHeight = MediaQuery.of(context).size.height;
ScreenState state =
Provider.of<ProviderPageStateController>(context).pageState;
// Loading state
if (state == ScreenState.Loading) {
return Scaffold(
body: Container(
color: Colors.purple,
child: Center(child: Text('Loading...')),
));
}
// SuccessfullyLoaded state
if (state == ScreenState.SuccessfullyLoaded) {
return Scaffold(
body: Container(
color: Colors.green,
child: Center(
child: Column(
children: [
Padding(
padding:
EdgeInsets.fromLTRB(0, (screenHeight / 2) - 100, 0, 0),
),
Text('Loaded!', key: Key('LoadedString')),
Container(
margin: EdgeInsets.all(25),
color: Colors.blueAccent,
child: TextButton(
child: Text(
'Reset',
style: TextStyle(color: Colors.white, fontSize: 20.0),
),
onPressed: () {
Provider.of<ProviderPageStateController>(context,
listen: false)
.updateState(ScreenState.InitialState);
},
),
),
],
))));
}
// InitialState (=default state)
return Scaffold(
body: Container(
color: Colors.white,
child: Center(
child: Column(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, (screenHeight / 2) - 100, 0, 0),
),
Container(
key: Key('ButtonA'),
margin: EdgeInsets.all(25),
color: Colors.blueAccent,
child: TextButton(
child: Text(
'Button A',
style: TextStyle(color: Colors.white, fontSize: 20.0),
),
onPressed: () {
Provider.of<ProviderPageStateController>(context,
listen: false)
.functionA();
},
),
),
],
),
),
));
}
}
This is the Controller:
import 'package:flutter/material.dart';
enum ScreenState { InitialState, Loading, SuccessfullyLoaded }
class ProviderPageStateController extends ChangeNotifier {
var pageState = ScreenState.InitialState;
void updateState(ScreenState screenState) {
pageState = screenState;
notifyListeners();
}
Future<void> functionA() async {
updateState(ScreenState.Loading);
Future.delayed(Duration(milliseconds: 1000), () {
updateState(ScreenState.SuccessfullyLoaded);
});
}
Future<void> reset() async {
updateState(ScreenState.InitialState);
}
}
This is the Widget test:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import '../provider_page/provider_page_state_controller.dart';
import '../provider_page/provider_page_widget.dart';
void main() {
testWidgets('ProviderPageWidget SuccessfullyLoaded test',
(WidgetTester tester) async {
final providerPageStateController = ProviderPageStateController();
await tester.pumpWidget(
ListenableProvider<ProviderPageStateController>.value(
value: providerPageStateController,
child: MaterialApp(
home: ProviderPageWidget(),
),
),
);
await providerPageStateController.functionA();
expect(providerPageStateController.pageState, ScreenState.Loading);
await tester.pumpAndSettle(Duration(seconds: 2));
expect(
providerPageStateController.pageState, ScreenState.SuccessfullyLoaded);
// Why does this assert fail?
expect(find.byKey(Key('LoadedString')), findsOneWidget);
});
}
Why is the last expect
of my Widget test failing?
I found out what the issue was. I was not pumping the correct widget in the widget test.
Updating the widget test to the following solved the issue:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import '../provider_page/provider_page_state_controller.dart';
import '../provider_page/provider_page_widget.dart';
void main() {
testWidgets('ProviderPageWidget SuccessfullyLoaded test',
(WidgetTester tester) async {
final providerPageStateController = ProviderPageStateController();
await tester.pumpWidget(
ListenableProvider<ProviderPageStateController>.value(
value: providerPageStateController,
child: MaterialApp(
home: HomePage(),
),
),
);
await providerPageStateController.functionA();
expect(providerPageStateController.pageState, ScreenState.Loading);
await tester.pumpAndSettle(Duration(seconds: 2));
expect(providerPageStateController.pageState, ScreenState.SuccessfullyLoaded);
// Why does this assert fail?
expect(find.byKey(Key('LoadedString')), findsOneWidget);
});
}
The change is on the line:
home: HomePage(),