Widget Testing With Flutter: Getting Started
In this tutorial about Widget Testing with Flutter, you’ll learn how to ensure UI widgets look and behave as expected by writing test code. By Stef Patterson.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Widget Testing With Flutter: Getting Started
20 mins
- Getting Started
- Exploring the Starter Project
- Unit Testing vs. Widget Testing
- Testing Pyramid
- Widget Testing the Car List
- Your First Test
- Injecting test data
- Ensuring visibility
- Widget Testing the Car List Page with Selection
- Negative Tests for Car List Page
- Verifying view update
- Widget Testing the Car Details Page for the Deselected Car
- Widget Testing Challenge
- Testing Details Page for Selected Cars
- Test that the Selected Car Updates the Widget
- Where to Go From Here?
Testing is important during your app development. As your product grows, it gets more complex, and performing manual tests becomes more difficult. Having an automated testing environment helps optimize this process.
Widget testing is like UI testing: You develop the look and feel of your app, ensuring every interaction the user makes produces the expected result.
For this tutorial, you’ll write widget tests for a car app called Drive Me, which lets users view a list of cars, view details about them and select and view specific cars. In the process, you’ll learn how to test that the app properly performs the following functions:
- Loads mock data to the widget tests.
- Injects erroneous mock data for negative tests.
- Ensures that the app presents a list of sorted cars and displays its details.
- Checks that the car selection appears correctly in the list page.
- Ensures that the car details page displays correctly.
Getting Started
To start, download the starter project by clicking the Download Materials button at the top or bottom of the tutorial, then explore the starter project in Visual Studio Code. You can also use Android Studio, but this tutorial uses Visual Studio Code in its examples.
Make sure to run flutter packages get
either at the command line or when prompted by your IDE. This pulls the latest version of the packages needed for this project.
Unused import
or variables not being used. Ignore these as they will be used by the time you have completed this tutorial.Build and run the project with flutter run
to familiarize yourself with how the app works.
Exploring the Starter Project
The starter project includes the implementation of the app so you can focus on widget testing. Take a look at the contents in lib to understand how the app works.
Starting at the bottom, as you know main.dart is the file where all Flutter apps start. dependency_injector.dart is where the app registers the main data layer classes and injects them via get_it
.
get_it
and how dependency injection works, read Unit Testing With Flutter.constants.dart contains most of the variables you’ll use throughout the app.
The project has four main folders:
- database
- details
- list
- models
In the lib/models folder, you’ll find an important file. car.dart is where the Car()
and CarsList()
model implementations reside. The CarsList()
model holds a list of cars and an error message if an exception occurs.
Next, look at lib/list/cars_list_bloc.dart. This is the CarsList()
data layer. CarsListBloc
loads data from the JSON found in assets/sample_data/data.json and passes it to the widget list. Thereafter, it sorts the cars alphabetically via alphabetizeItemsByTitleIgnoreCases()
.
In the lib/details folder is car_details_bloc.dart, which gets data from CarsListBloc
and passes it to the CarDetails
widget in car_details_page.dart.
Open lib/details/car_details_page.dart. You’ll see that on init
it retrieves the data passed in by CarDetailsBloc
and presents it on the widget. When users select or deselect items, CarsListBloc()
makes the updates.
When the user selects any car, a separate data stream manages it.
lib/database contains cars_database.dart, which implements an abstract class called CarsDataProvider
. This class contains loadCars()
that parses the JSON file containing a list of car data. The parsed data returned is a CarsList()
.
As you guessed it from some filenames, this project uses BLoC
to pass data between the widgets layer and the data layer.
BLoC
, visit Getting Started With BLoC Pattern.Now that you’ve tried the app and understand the implementation details, it’s time to start running some tests.
Before you dive deep into the topic of widget testing with Flutter, take a step back and compare it with unit testing.
Unit Testing vs. Widget Testing
Unit testing is a process where you check for quality, performance or reliability by writing extra code that ensures your app logic works as expected. It tests for logic written in functions and methods. The unit tests then grow and accumulate to cover an entire class and subsequently a huge part of the project, if not all.
The goal of a widget test is to verify that every widget’s UI looks and behaves as expected. Fundamentally, you perform tests by re-rendering the widgets in code with mock data.
This also tells you that if you modify the logic of the app — for example, you change the login validation of the username from a minimum of six characters to seven — then your unit test and widget test may both fail together.
Tests lock down your app’s features, which help you to properly plan your app’s design before developing it.
Testing Pyramid
There are three types of tests you can perform with Flutter:
- Unit tests: Used to test a method or class.
- Widget tests: These test a single widget.
- Integration tests: Use these to test the critical flows of the entire app.
So, how many tests will you need? To decide, take a look at the testing pyramid. It summarizes the essential types of tests a Flutter app should have:
Essentially, unit tests should cover most of the app, then widget tests and, lastly, integration tests.
Even when good testing grounds are in place, you shouldn’t omit manual testing.
As you go up the pyramid, the tests get less isolated and more integrated. Writing good unit tests help you build a strong base for your app.
Now that you understand the need for testing, it’s time to dive into the project for this tutorial!
Widget Testing the Car List
Open test/list/cars_list_bloc_test.dart. Look below // TODO 3: Unit Testing Data Loading Logic
and you’ll see the unit tests implemented in this project. These unit tests ensure that the data structure you provide to the widget is accurate.
Before going into writing the test scripts, it’s good to look at the actual screen you’re testing. In test/database/mock_car_data_provider.dart, the user has selected the first car — the Hyundai Sonata 2017, shown the image below:
Are you ready to start adding widget tests?