unit-testing - Flutter 的 WidgetTester.pumpAndSettle() 不等待渲染完成?

标签 unit-testing testing flutter dart widget

我有一个 StatefulWidget,它根据加载状态(加载 -> 加载/错误)呈现不同的 Widget:

// widget
class ListNotesScreen extends StatefulWidget {
  static const route = '/listNotes';
  static navigateTo(BuildContext context, [bool cleanStack = true]) =>
      Navigator.pushNamedAndRemoveUntil(context, route, (_) => !cleanStack);

  final String title;
  final ListNotesUseCase _useCase;
  final VoidCallback _addNoteCallback;
  ListNotesScreen(this._useCase, this._addNoteCallback, {Key key, this.title}) : super(key: key);

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

// state
class _ListNotesScreenState extends State<ListNotesScreen> {
  ListNotesLoadState _state;

  Future<ListNotesResponse> _fetchNotes() async {
    return widget._useCase.listNotes();
  }

  @override
  initState() {
    super.initState();
    _loadNotes();
  }

  _loadNotes() {
    setState(() {
      _state = ListNotesLoadingState();
    });

    _fetchNotes().then((response) {
      setState(() {
        _state = ListNotesLoadedState(response.notes);
      });
    }).catchError((error) {
      setState(() {
        _state = ListNotesLoadErrorState(error);
      });
    });
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
      title: Text('Notes list'),
      actions: <Widget>[
        IconButton(icon: Icon(Icons.add), onPressed: widget._addNoteCallback),
        IconButton(icon: Icon(Icons.refresh), onPressed: () => _loadNotes())
      ],
    ),
    body: _state.getWidget());
}

// loading states
// State:
@sealed
abstract class ListNotesLoadState {
  Widget getWidget();
}

// Loading
class ListNotesLoadingState extends ListNotesLoadState {
  @override
  Widget getWidget() => Center(child: CircularProgressIndicator(value: null));
}

// Loaded
class ListNotesLoadedState extends ListNotesLoadState {
  final List<Note> _notes;
  ListNotesLoadedState(this._notes);

  @override
  Widget getWidget() => ListView.builder(
    itemBuilder: (_, int index) => NoteItemWidget(this._notes[index]),
    itemCount: this._notes.length,
    padding: EdgeInsets.all(18.0));
}

这是小部件的测试:

void main() {
  testWidgets('Notes list is shown', (WidgetTester tester) async {
    final title1 = 'Title1';
    final title2 = 'Title2';
    final body1 = 'Body1';
    final body2 = 'Body2';
    var notes = [
      Note('1', title1, body1),
      Note('2', title2, body2),
    ];
    final listUseCase = TestListNotesInteractor(notes);
    final widget = ListNotesScreen(listUseCase, null, title: 'List notes');
    await tester.pumpWidget(widget);
    await tester.pumpAndSettle();

    expect(find.text('someInvalidString'), findsNothing);
    expect(find.text(title1), findsOneWidget);
    expect(find.text(title2), findsOneWidget);
    expect(find.text(body1), findsOneWidget);
    expect(find.text(body2), findsOneWidget);

    // TODO: fix the test (tested manually and it works)
  });
}

所以小部件测试器应该等到它在 initState() 中设置为加载的状态,然后 _loadNotes 将其移动到 ListNotesLoadedState 并且ListNotesLoadedState.getWidget() 返回带有预期字符串的 ListView(NoteItemWidget root 和一些带有预期字符串的 Text)。

但是测试失败了。原因是什么(我能够在应用程序中使用测试交互器并直观地看到预期的文本)?我如何在测试失败时分析实际的 Widgets 树?

我倾向于认为 WidgetTester 没有等待 Future 完成(虽然它预计会被模拟并在幕后同步,请纠正我)。

可以找到 project on Github (确保调用 flutter packages pub run build_runner build 生成 json 反序列化代码)。

最佳答案

我找到了原因:MaterialApp(或者可能是任何应用程序)应该是小部件树的根!

final widget = MaterialApp(home: ListNotesScreen(interactor, null)); // succeeds

代替:

final widget = ListNotesScreen(interactor, null); // fails

我还删除了未使用的 title 属性,因此测试代码与我最初使用的代码有点不同:

final widget = ListNotesScreen(listUseCase, null, title: 'List notes');

the docs 中没有提到(实际上是这个原因吗?)虽然测试代码有它。如果我遗漏了什么,请告诉我。

关于unit-testing - Flutter 的 WidgetTester.pumpAndSettle() 不等待渲染完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59339054/

相关文章:

unit-testing - Microsoft 的 Entity Framework 如何抑制测试驱动开发?

Flutter IAP 未在 TestFlight 上显示

mysql - 如何在 Node js 中使用一些模拟数据和 mocha stub mysql 查询

c - 单元测试 : cohabitation of the production-code and the mocked implementation

java - 如何从 JMockit 模拟静态方法

Bash 测试参数是否存在

react-native - 我可以在 flutter 中获得与 React Native 中的 <TouchableOpacity/> 相同的效果吗?

flutter - 如何从 Flutter Firebase 实时数据库读取数据?

database - Symfony 3为单元测试创​​建数据库

ios - XCTests过早取消