micromuseum.cc: testing flutter components
In the best tradition of TDD I’m going to write components along with component tests for them. Flutter provides a decent testing framework (the flutter_test library ) allowing to write unit, component and integration tests.
See also
Some good references about testing in flutter:
Here some details about different kind of tests:
- Unit tests: don’t expose flutter, and are meant to test business logic. They use test/test.dart Rather than flutter_test.
- Component (widget) tests: these test are based on flutter’s WidgetTester, a nifty class that allows to “pump” events to the widget you are testing as well as to look for children in the widget hierarchy
- Integration tests: with these tests you start an "Instrumented" version of the App that allows to plug different sensors while running the app, measure performance and replicate the actual app working environment.
While there aren't currently unit tests, we can see an example of a component test:
Testing DotsIndicator component
Note
The following DotsIndicator component is adapted from one found on github: https://gist.github.com/collinjackson/4fddbfa2830ea3ac033e34622f278824
I later found out that there's also a component on Pub: https://pub.dartlang.org/packages/dots_indicator
This is the component code:
Component
import 'package:flutter/material.dart'; import 'dart:math'; /// An indicator showing the currently selected page of a PageController class DotsIndicator extends AnimatedWidget { DotsIndicator({ this.controller, this.itemCount, this.onPageSelected, this.color: Colors.black, }) : super(listenable: controller); /// The PageController that this DotsIndicator is representing. final PageController controller; /// The number of items managed by the PageController final int itemCount; /// Called when a dot is tapped final ValueChanged<int> onPageSelected; /// The color of the dots. /// /// Defaults to `Colors.white`. final Color color; // The base size of the dots static const double _kDotSize = 8.0; // The increase in the size of the selected dot static const double _kMaxZoom = 1.5; // The distance between the center of each dot static const double _kDotSpacing = 25.0; Widget _buildDot(int index) { double page = (controller.page != null) ? controller.page % itemCount : 0; double selectedness = Curves.easeOut.transform( max( 0.0, 1.0 - ((page ?? controller.initialPage) - index).abs(), ), ); double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness; return new Container( width: _kDotSpacing, child: new Center( child: new Material( color: zoom > 1.1 ? color : color.withOpacity(0.1), type: MaterialType.circle, child: new Container( width: _kDotSize * zoom, height: _kDotSize * zoom, child: new InkWell( onTap: () => onPageSelected(index), ), ), ), ), ); } Widget build(BuildContext context) { return new Row( mainAxisAlignment: MainAxisAlignment.center, children: new List<Widget>.generate(itemCount, _buildDot), ); } }
The widget builds a number of dots and applies a zoom effect to the selected dot. When a dot is clicked a callback is invoked.
And the following is the component test:
Component Test
import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mm_mobile/components/dotsindicator.dart'; import 'package:mockito/mockito.dart'; class MockPageController extends Mock implements PageController {} void main() { testWidgets("Correct number of dots is displayed", (WidgetTester tester) async { final MockPageController ctrl = new MockPageController(); final int itemCount = 10; when(ctrl.page).thenReturn(0); when(ctrl.initialPage).thenReturn(0); DotsIndicator dots = new DotsIndicator(controller: ctrl, itemCount: itemCount, onPageSelected: (int i) {}); await tester.pumpWidget(MaterialApp(home:dots)); expect(find.byType(Material), findsNWidgets(itemCount)); }); testWidgets("Clicking on a dots shows the correct page", (WidgetTester tester) async { final MockPageController ctrl = new MockPageController(); final int itemCount = 10; int selected = -1; when(ctrl.page).thenReturn(0); when(ctrl.initialPage).thenReturn(0); DotsIndicator dots = new DotsIndicator(controller: ctrl, itemCount: itemCount, onPageSelected: (int i) { selected = i; }); await tester.pumpWidget(MaterialApp(home:dots)); await(tester.tap(find.byType(Material).first)); expect(selected, equals(0));q // expect(find.byType(Material), findsNWidgets(itemCount)); await(tester.tap(find.byType(Material).last)); expect(selected, equals(9)); }); }
Comments