flutter 如何在单元测试期间访问StatefulWidget的State的内容?

0md85ypi  于 2023-01-09  发布在  Flutter
关注(0)|答案(5)|浏览(217)

我有一个StatefulWidget(称为MyWidget),其状态(MyWidgetState)具有字段myData,该字段在initState()期间初始化,如下所示:

void initState() {
    super.initState();
    myData = new myData(config.someField.getId());
}

当用户按下按钮时,myData将被添加到全局列表中或从全局列表中删除。
我试着写一个单元测试来测试这个行为,但是我不知道如何访问MyWidgetState。我试着把它包含在setup()中:

widget = MyWidget();
widgetState = widget.createState(); 
widgetState.init();

但每次尝试initState()时它都会崩溃,并抱怨“someField被null调用”。这没什么。我可能是在作弊,我应该用WidgetBuilder做一些事情,或者使用MyWidget启动一个应用程序,然后在树中找到MyWidget,一旦它被正确示例化。
如果我完成了所有这些操作,那么一旦我完成了这些操作,我如何才能访问MyWidget的MyWidgetState来获得myData的副本并将其与全局列表进行比较呢?

mrzz3bfm

mrzz3bfm1#

您可以创建一个状态,然后访问它的内容。按照Ian Hickson答案。下面是一个实现示例:

final MyWidgetState myWidgetState = tester.state(find.byType(MyWidget));

然后您可以访问状态内容:

myWidgetState.myData;

您可以在Flutter的repo中找到更多示例。

f87krz0w

f87krz0w2#

使用tester.pumpWidgets创建小部件,然后使用tester.state(find.foo)查找State(其中find.foo是查找小部件的查找器)。有关更多选项,请参阅WidgetTester文档。

dzjeubhm

dzjeubhm3#

如果你想在小部件状态的一个方法上写一个单元测试,可以这样做:

// Assuming your stateful widget is like this:
class MyWidget extends StatefulWidget {
  const MyWidget({this.someParam, Key? key}) : super(key: key);
  final String someParam;

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

@visibleForTesting
class MyWidgetState extends State<MyWidget> {
  int methodToBeTested() {
    // dummy implementation that uses widget.XXX
    if (widget.someParam.isEmpty) return 0;
    return 1;
  }

  @override
  Widget build(BuildContext context) {
    // return ...
  }
}

// In your test file
void main() {
  test('if widget.someParam is empty, do something', () {
    const widget = MyWidget(someParam: '');
    final element = widget.createElement(); // this will set state.widget
    final state = element.state as MyWidgetState;
    expect(state.methodToBeTested(), 0);
  });
}
omqzjyyz

omqzjyyz4#

如果你使用代码片段在Flutter中创建一个有状态的小部件,你可能已经注意到Flutter创建的状态类是一个私有类,在开头用下划线标记。这很好,因为这样的类不应该在库外使用--除了测试。
在小部件/集成测试中,你可能希望访问状态类的变量。在这种情况下,将状态类标记为private意味着你不能直接访问这些变量。这可能不是你想要的。为了两全其美,我们可以使用@visibleForTesting装饰器。
下面是一个例子。

import 'dart:math';
import 'package:flutter/material.dart';

class CounterWidget extends StatefulWidget {
  const CounterWidget({Key key}) : super(key: key);

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

@visibleForTesting
class CounterWidgetState extends State<CounterWidget> {
  int counter;
  @override
  void initState() {
    super.initState();
    var rndGen = Random(79);
    counter = rndGen.nextInt(96);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: ElevatedButton(
            key: Key('incrementButton'),
            onPressed: () {
              setState(() {
                counter++;
              });
            },
            child: Text(
              'Increment($counter)',
            )));
  }
}

@visibleForTesting的文档说明
用于注解已公开的声明,以便使其比其他情况更明显,从而使代码可测试。
分析器等工具可以在以下情况下提供反馈
注解与不在包的lib文件夹中的声明、或私有声明、或未命名的静态扩展中的声明相关联,或声明在其定义库或在定义包的test文件夹中的库之外被引用。
这里的要点是,如果您在库或测试之外使用公共状态类,Dart分析器将发出警告,这在Flutter设计中很少是必需的。
使用visibleForTesting的想法来自here

2q5ifsrm

2q5ifsrm5#

下面是一个完整的工作示例,它在widget test测试状态值
这可能不是理想的方法,因为 widget statestate variable 都是公共的,但是它是有效的,特别是,确保widget state和state变量都是公共的,以便测试访问它们,所以没有前导下划线(_)。

带有公共状态/状态变量的应用程序/小部件定义

// main.dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => MyHomePageState();
}

// Note: this must not be a private member,
// so no leading underscore (_)
class MyHomePageState extends State<MyHomePage> {
  // Note: this must not be a private member,
  // so no leading underscore (_)
  int counter = 0;

  void _incrementCounter() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

访问公共状态变量的单元测试

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:state_test/main.dart';

void main() {
  testWidgets('HomePage counter increments when + button pressed', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(const MyApp());

    // Get widget state for validation
    final MyHomePageState myHomePageState =
        tester.state(find.byType(MyHomePage));

    // Verify that our counter starts at 0
    expect(myHomePageState.counter, 0);

    // Tap the '+' icon to increment the counter
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented
    expect(myHomePageState.counter, 1);
  });
}

相关问题