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

时间:2017-02-09 03:52:57

标签: flutter

我有一个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的副本并将其与全局列表进行比较?

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

如果您想对小部件状态的其中一种方法编写单元测试,可以通过以下方式完成:

// 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);
  });
}

答案 2 :(得分:1)

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

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

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

myWidgetState.myData;

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

答案 3 :(得分:1)

如果您使用任何代码片段在 Flutter 中创建有状态的小部件,您可能已经注意到 Flutter 创建的状态类是一个私有类,它在开头由下划线构成。这很好,因为此类类不应在库外使用 - 除了测试。

在小部件/集成测试中,您可能希望访问状态类的变量。在这种情况下,将状态类标记为私有意味着您不能直接访问这些变量。这可能是不希望的。为了两全其美,我们可以使用 @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 文件夹中的声明相关联 包、私有声明或未命名的声明 静态扩展,或声明在其外部引用 定义库或位于测试文件夹中的库 定义包。

这里的关键是,如果您在库或测试之外使用公共状态类,Dart 分析器会警告您,这在 Flutter 设计中很少是必需的。

使用 visibleForTesting 的想法来自于 here