#flutter #unit-testing
Вопрос:
В настоящее время я пишу свое первое приложение Flutter и добавляю первые в истории модульные тесты. Это также мой первый вопрос о StackOverflow, поэтому, пожалуйста, дайте мне знать, если я не предоставил достаточной информации (или слишком много информации!).
Мой домашний экран состоит из двух основных виджетов, которые я разделил на два разных класса. Я хотел бы написать ряд тестов для рабочего стола, начиная с первого главного виджета, который содержит две кнопки для прокрутки по месяцам. Я протестировал это вручную на физическом Android, iphone, а также на симуляторах — месяцы прокручиваются и работают так, как я ожидаю.
Это начальный экран с двумя основными классами:
//home_screen.dart class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override _HomeScreenState createState() =gt; _HomeScreenState(); } class _HomeScreenState extends Statelt;HomeScreengt; { DateTime _maxDate = DateTime.now(); int _month = DateTime.now().month; int _year = DateTime.now().year; void setMMYY(int mm, int yy) { setState(() { _month = mm; _year = yy; }); } void getMaxDate() { //set the max date } @override void initState() { getMaxDate(); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(constants.APP_NAME), ), body: Column( children: [ Expanded( flex: 1, child: MonthController(setMMYY, _month, _year, _maxDate)), Expanded( flex: 9, child: MyData(_month, _year), ), ], ), ); } }
Это класс MonthController, который будет соответственно увеличивать/уменьшать месяц и отображать его в виджете с центральным текстом:
//month_controller2.dart class MonthController2 extends StatelessWidget { final Function(int, int) setMMYY; int month; int year; DateTime maxDate; MonthController2(this.setMMYY, this.month, this.year, this.maxDate, {Key? key}) : super(key: key); void _nextMonth() { if (month == 12) { month = 1; year = 1; } else { month = 1; } setMMYY(month, year); } void _prevMonth() { if (month == 1) { month = 12; year -= 1; } else { month -= 1; } setMMYY(month, year); } bool _isPastMaxDate() { DateTime thisMonth = DateTime(year, month, 1); DateTime nextMonth = DateTime(year, thisMonth.month 1, 1); if (nextMonth.isAfter(maxDate)) { return true; } return false; } bool _isBeforeToday() { DateTime thisMonth = DateTime(year, month, 1); DateTime prevMonthTemp = thisMonth.subtract( const Duration(days: 2), ); DateTime prevMonth = DateTime(prevMonthTemp.year, prevMonthTemp.month, 1); DateTime todayMonth = DateTime(DateTime.now().year, DateTime.now().month, 1); if (prevMonth.isBefore(todayMonth)) { return true; } return false; } @override Widget build(BuildContext context) { var _theme = Theme.of(context); return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: _isBeforeToday() ? null : _prevMonth, child: const Icon(Icons.keyboard_arrow_left_rounded), key: const Key("DECREMENT_MONTH"), ), Container( width: MediaQuery.of(context).size.width * 0.6, alignment: Alignment.center, child: FittedBox( fit: BoxFit.fitWidth, child: Text( DateFormat.yMMMM().format(DateTime(year, month)), style: _theme.textTheme.headline6, ), ), ), ElevatedButton( onPressed: _isPastMaxDate() ? null : _nextMonth, child: const Icon(Icons.keyboard_arrow_right_rounded), key: const Key("INCREMENT_MONTH"), ), ], ); } }
This is the test that I have written to test that the months are increasing as expected when you tap on the increment month ElevatedButton. However, the test fails. When I adjust the test to test for thisMonth.year, thisMonth.month, it passes. This suggests to me that tester.tap has not changed the text or there is no «rebuild» happening. Why is that? I have tried to add longer durations to tester.pump / tester.pumpAndSettle but it does not appear to be working.
//home_screen_test.dart void main() { var thisMonth = DateTime(DateTime.now().year, DateTime.now().month, 1); var nextMonthTemp = thisMonth.add(const Duration(days: 35)); var nextMonth = DateTime(nextMonthTemp.year, nextMonthTemp.month, 1); var prevMonthTemp = thisMonth.subtract(const Duration(days: 2)); var prevMonth = DateTime(prevMonthTemp.year, prevMonthTemp.month, 1); var maxDate = thisMonth.add(const Duration(days: 100)); Widget createWidgetForTesting({required Widget child}) { return MaterialApp( home: child, ); } testWidgets('MonthController should increment month', (WidgetTester tester) async { await tester.pumpWidget( createWidgetForTesting( child: HomeScreen(), ), ); await tester.pumpAndSettle(); await tester.tap(find.byKey(const Key("INCREMENT_MONTH"))); await tester.pumpAndSettle(); expect( find.text( DateFormat.yMMMM().format( DateTime(nextMonth.year, nextMonth.month), ), ), findsOneWidget); }); }
Error message:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════ The following TestFailure object was thrown running a test: Expected: exactly one matching node in the widget tree Actual: _TextFinder:lt;zero widgets with text "November 2021" (ignoring offstage widgets)gt; Which: means none were found but one was expected When the exception was thrown, this was the stack: #4 main.lt;anonymous closuregt; (file:///....../test/home_screen_test.dart:47:5) lt;asynchronous suspensiongt; lt;asynchronous suspensiongt; (elided one frame from package:stack_trace) This was caught by the test expectation on the following line: file:///....../test/home_screen_test.dart line 47 The test description was: MonthController should increment month
Отдельно я попытался написать тест для month_controller_test.вместо этого дарт. Чтобы тест работал, мне пришлось изменить month_controller.dart на виджет с отслеживанием состояния. Хотя это, вероятно, нормально, я все же хотел бы понять, почему вышеперечисленное не сработало.