[英]Widget not rebuilding on notifyListeners during test
I'm trying to build a Widget test for a screen that's using the Provider framework.我正在尝试为使用 Provider 框架的屏幕构建 Widget 测试。
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.该应用程序有 1 个屏幕和 1 个按钮,当我点击按钮时,它会触发 function 来更新 state。更新 state 后,将出现一个带有键Key('LoadedString')
的字符串。
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.当我尝试使用小部件测试来验证预期行为时,UI 似乎没有更新。
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:这是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?为什么我的 Widget 测试的最后expect
失败了?
Use the Provider.value
constructor, since you are using a variable instead of instantiating an object. And to make sure all dependants are updated eagerly use the the lazy: false
flag in tests, even when the value is not accessed .使用Provider.value
构造函数,因为您使用的是变量而不是实例化 object。为了确保所有依赖项都得到及时更新,请在测试中使用lazy: false
标志,即使未访问该值也是如此。 The resulting changes are:由此产生的变化是:
void main() {
final providerPageState = ProviderPageState();
...
Provider<ProviderPageState>.value(
value: providerPageState,
child: MaterialApp(
home: ProviderPageWidget(),
),
lazy: false,
),
);
I would also recommend switching to ChangeNotifierProvider
, when using provider to store states.我还建议在使用提供者存储状态时切换到ChangeNotifierProvider
。 Make sure your ProviderPageState
is extending ChangeNotifier
and implementing the needed methods, as reference you can use the implementation of ValueNotifier
.确保您的ProviderPageState
正在扩展ChangeNotifier
并实现所需的方法,作为参考,您可以使用ValueNotifier
的实现。
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(),主页:主页(),
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.