flutter测试发现器没有找到我的状态小部件

发布于 2025-02-12 21:37:04 字数 2002 浏览 1 评论 0原文

我正在关注本教程 https://docs.flutter.dev/development/ui/interactive#the-parent-widget-manages-manages-the-widgets-state ,我在main.dart :

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    Widget statesSection = Container(
        padding: const EdgeInsets.all(32),
        child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [StatefulParentWidget()]));
    return MaterialApp(
        title: 'Flutter Layout',
        home: Scaffold(
            appBar: AppBar(title: const Text("Flutter Layout")),
            body: ListView(children: [
              statesSection
            ])));
  }

它在以下测试代码中根本找不到任何内容:

testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(const MyApp());
    expect(find.byType(StatefulParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
});

测试错误消息:

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching node in the widget tree
  Actual: _WidgetTypeFinder:<zero widgets with type "StatefulParentWidget" (ignoring offstage
widgets)>
   Which: means none were found but one was expected

任何建议和洞察力都将受到赞赏。 https://github.com/khteh/flutter

I am following this tutorial https://docs.flutter.dev/development/ui/interactive#the-parent-widget-manages-the-widgets-state and I have the following in main.dart:

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    Widget statesSection = Container(
        padding: const EdgeInsets.all(32),
        child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [StatefulParentWidget()]));
    return MaterialApp(
        title: 'Flutter Layout',
        home: Scaffold(
            appBar: AppBar(title: const Text("Flutter Layout")),
            body: ListView(children: [
              statesSection
            ])));
  }

It doesn't find anything at all in the following test code:

testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(const MyApp());
    expect(find.byType(StatefulParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
});

Test error message:

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure was thrown running a test:
Expected: exactly one matching node in the widget tree
  Actual: _WidgetTypeFinder:<zero widgets with type "StatefulParentWidget" (ignoring offstage
widgets)>
   Which: means none were found but one was expected

Any advice and insight is appreciated. https://github.com/khteh/flutter

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

迷乱花海 2025-02-19 21:37:04

大结局

将您的代码更改为:

              // titleSection,
              // buttonsSection,
              // textSection,
              statesSection

并且测试将通过,

  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(const MyApp());
    expect(find.byType(Scaffold), findsOneWidget);
    expect(find.byType(StatefulParentWidget), findsOneWidget);

    expect(find.text("Inactive"), findsOneWidget);
    expect(find.text("Active"), findsNothing);
    //StatefulParentWidget statefulParentWidget = const StatefulParentWidget();
    //tester.state(find.byWidget(statefulParentWidget));
    //expect(find.byWidget(tapboxB), findsOneWidget);
  });

因此测试只能找到已经渲染的小部件,在您的情况下,statessection中的小部件曾经脱离舞台。

以前的讨论

1与aCkaffod

如果您面对与以下相同的例外消息,

flutter: (The following exception is now available via WidgetTester.takeException:)
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building MainPage:
flutter: No MediaQuery widget ancestor found.
flutter: Scaffold widgets require a MediaQuery widget ancestor.

请将您的测试代码更改为此处,它应该

void main() {
  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MaterialApp(
      home: const MainPage(),
    ));
    expect(find.byType(ParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
  });
}

在此处使用完整代码:

void main() {
  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MaterialApp(home: const MainPage(),));
    expect(find.byType(ParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
  });
}

class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Hello"),
        ),
        body: Container(
            padding: const EdgeInsets.all(32),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: const [ParentWidget()])));
  }
}

// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  const TapboxB({
    super.key,
    this.active = false,
    required this.onChanged,
  });

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            style: const TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

2

如果您只想仅测试窗口小部件,则必须替换文本窗口小部件与此:

          child: Text(
            active ? 'Active' : 'Inactive',
            textDirection: TextDirection.ltr,
            style: const TextStyle(fontSize: 32.0, color: Colors.white),
          ),

在我的第一个示例中,scallfold可以做到这一点,因此您可以在不说textDirection属性的情况下进行测试。请参阅 textDirection for for进一步阅读。

完整代码在这里:

void main() {
  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(statesSection);
    expect(find.byType(ParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
  });
}

Widget statesSection = Container(
    padding: const EdgeInsets.all(32),
    child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [ParentWidget()]));

class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Hello", textDirection: TextDirection.ltr),
        ),
        body: Container(
            padding: const EdgeInsets.all(32),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: const [ParentWidget()])));
  }
}

// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  const TapboxB({
    super.key,
    this.active = false,
    required this.onChanged,
  });

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            textDirection: TextDirection.ltr,
            style: const TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

Finale

Change your code to:

              // titleSection,
              // buttonsSection,
              // textSection,
              statesSection

and the test will pass for

  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(const MyApp());
    expect(find.byType(Scaffold), findsOneWidget);
    expect(find.byType(StatefulParentWidget), findsOneWidget);

    expect(find.text("Inactive"), findsOneWidget);
    expect(find.text("Active"), findsNothing);
    //StatefulParentWidget statefulParentWidget = const StatefulParentWidget();
    //tester.state(find.byWidget(statefulParentWidget));
    //expect(find.byWidget(tapboxB), findsOneWidget);
  });

So the test only can find widgets rendered already, and in your case, the widgets in statesSection were once off the stage.

Former Discussion

1 With Scaffod

If you were facing the same exception message as follows:

flutter: (The following exception is now available via WidgetTester.takeException:)
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building MainPage:
flutter: No MediaQuery widget ancestor found.
flutter: Scaffold widgets require a MediaQuery widget ancestor.

Change your test code to this, it should work

void main() {
  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MaterialApp(
      home: const MainPage(),
    ));
    expect(find.byType(ParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
  });
}

Full code is here:

void main() {
  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MaterialApp(home: const MainPage(),));
    expect(find.byType(ParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
  });
}

class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Hello"),
        ),
        body: Container(
            padding: const EdgeInsets.all(32),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: const [ParentWidget()])));
  }
}

// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  const TapboxB({
    super.key,
    this.active = false,
    required this.onChanged,
  });

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            style: const TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

2 Without Scaffod

If you want to test the widget solely, then you have to replace your Text widget with this:

          child: Text(
            active ? 'Active' : 'Inactive',
            textDirection: TextDirection.ltr,
            style: const TextStyle(fontSize: 32.0, color: Colors.white),
          ),

In my first example, the Scallfold does the trick so you can test without telling textDirection attribution. Please refer to TextDirection enum for further reading.

Full code is here:

void main() {
  testWidgets('State management tests', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(statesSection);
    expect(find.byType(ParentWidget), findsOneWidget); // Fails!
    expect(find.text("Inactive"), findsOneWidget); // Fails!
    expect(find.text("Active"), findsNothing); // Fails!
  });
}

Widget statesSection = Container(
    padding: const EdgeInsets.all(32),
    child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [ParentWidget()]));

class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Hello", textDirection: TextDirection.ltr),
        ),
        body: Container(
            padding: const EdgeInsets.all(32),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: const [ParentWidget()])));
  }
}

// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  const TapboxB({
    super.key,
    this.active = false,
    required this.onChanged,
  });

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            textDirection: TextDirection.ltr,
            style: const TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文