Testing in Flutter
One of a series of articles detailing a comprehensive starter app. This app is generated by a template offered by the package, app_template, which uses the underlying framework, mvc_application, based on the MVC design pattern.
I Like Screenshots. Tap Caption For Gists.
As always, I prefer using screenshots in my articles over gists to show concepts rather than just show code. I find them easier to work with frankly. However, you can click or tap on their captions to see the code in a gist or in Github. Tap or click on the screenshots themselves to zoom in on them for a closer look.
No Moving Pictures, No Social Media
There will be gif files in this article demonstrating aspects of the topic at hand. However, it’s said viewing such gif files is not possible when reading this article on platforms like Instagram, Facebook, etc. They may come out as static pictures or simply blank placeholder boxes. Please, be aware of this and maybe read this article on medium.com
Test Your Flutter
Included in the package, app_template, is the file, widget_test.dart, that‘s found in every new Flutter project under the directory, test. This article will briefly review this file conveying the integration testing, the widget testing, and the unit testing that’s performed every time this package is uploaded to github.com with new changes. Testing your code is an integral part of development, and this Dart package is no exception. For further reference, you have the webpage, Testing Flutter apps.
There’s A Test For That
Below is a screenshot of the widget_test.dart file at the time of this writing. The gif files below are the result when running this file. This implies this file runs an Integration test (also called end-to-end testing or GUI testing) as it goes through the full app executing its many functions and features verifying its widgets and other aspects of the app successfully work together as a whole.
Testing Extensive Tests
As you see in the screenshot below, this package doesn’t just have the one Dart file in the directory, test, but a number of files and folders that make up the testing. All these test files are then referenced in one ‘export file’ called, view.dart.
The screenshot below is the beginning of the ‘main test file’, widget_test.dart. The testing begins by starting up the app’s main class, TemplateApp. The WidgetTester class object, tester, then calls the function, pumpAndSettle, to wait for the app to settle down after starting up before the testing continues.
Note the ‘Controller’ object instantiated in the test file (the second arrow below), is a very important player in this whole enterprise. One can say it is responsible for the very ‘logic’ of the app — responding to both user and system events during runtime. And when it comes to testing, it‘ll prove to be very useful in orchestrating the testing involved.
For example, the instance variable, con, has the property, application, used by the app itself to determine which of the three internal apps to run. Such a property is useful in the testing — dictating which of the three apps is tested next. See below. The testing will take each app, in turn, and will call separate functions performing specific tests for each.
A Singleton Approach
It’s important to note, I generally follow the singleton design pattern when composing such Controller objects. That means instantiating only one instance of this class for the duration of the application’s lifetime with the use of a factory constructor (see below). You see, the role of such a class only needs one instance really, and as such further benefits in the app’s testing. Such a class can be instantiated, again and again, if necessary to help with testing. The TemplateController, as well as the WordPairsController both, use a factory constructor. See below.
Keep The Count
As another example, the ‘Counter App’ Controller is also used for testing. Because it too is instantiated only once, it retains its current counter value, and so the expression,
CounterController().data, is available to ensure that the count is successfully incremented in the test below. A screenshot of the ‘Counter’ Controller below also reveals a factory constructor.
Test The Widget
This main course of tests actually works through the app’s interface and executes its specific functions and features. In other words, particular buttons and menu items are literally being ‘tapped’ in the test environment. Since a Flutter app’s interface is made up of mostly Widgets, it’s possible in many cases to isolate and ‘tap’ on particular widgets and run their intended function for testing. A wonderful feature of the Flutter platform.
Tap Or Call
An example of this is when tapping on the heart icons for the first three word-pairs listed in the ‘Word Pairs’ app. Mind you, this only happens in the Material interface. When testing in the Cupertino interface, the three CupertinoListTile widgets are explicitly drawn out and their onTap() functions are called directly (see first screenshot below). This is an example of Widget testing. The corresponding onTap() function is in the second screenshot below.
Direct Your Testing
The function responsible for deleting a contact entry (see below) demonstrates the interaction available to you when testing your app using an established underlying framework. It highlights Flutter’s test environment as well. How you can retrieve specific Widgets and call their defined functions and properties. For example, access to the ‘Contacts’ Controller determines if there are any contacts to delete in the first place. Lastly, you can see how the property,
App.useMaterial, directs what ‘type’ of Widget is retrieved during the testing.
The Source Is Key
Looking at the many red arrows below, you can see at many times the testing retrieves Widgets by its Local key. It’s a ready and reliable means to retrieve Widgets for testing, and I take full advantage of this particularly in the app’s popup menu. In the second screenshot below, each PopupMenuItem object is assigned a Local key. It’s then in the many functions involved in the testing where those key values are again referenced to retrieve the Widgets.
The Flutter platform is a wonder. The concept of Widgets makes the testing of such code that much easier, and with a good underlying framework, that makes testing that much more efficient and effective.