简体   繁体   English

小部件在测试期间不在 notifyListeners 上重建

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM