Test-driven Development in Flutter
Table of contents
Introduction
In this article, I will describe the steps of implementation of Test-driven Development (TDD) in your Flutter project and also cover a basic introduction to the subject.
What is TDD?
TDD or Test-driven Development is the practice of writing quality assurance test cases for each unit of the app before any feature or code is implemented.
Need for TDD
Test-driven development is beneficial because of the following advantages:
- Generates minimal Bugs
- Identifies bugs in the early phase
- Reduces repetitive debugging
- Makes code more reliable with refactoring
Types of Testing
Unit Testing
Unit testing is conducted to check the implementation or functionality of a single class, method, or function.
Widget Testing
Widget testing is conducted to check the UI or widgets and components used to build a screen.
Integration Testing
Integration tests are written to check the application's complete functionality. It is also called end-to-end or GUI testing.
Additional Types Of Testing
Security Testing
Security tests are meant to test the authentication, access, and code injections for the complete application. It makes an app that is ready to deploy highly secure.
Smoke Testing
Smoke testing is written in order to check the complete or core functionality of an application in a precise way.
Process of Test-driven Development
The process of Test-driven Development tends to be iterative and loopy (i.e., the process will be in a loop until the conditions or test cases that are mentioned are passed).
There are mainly three phases in TDD:
1. Red Phase In this phase, the developer needs to write test scenarios for features that need to be created. Initially, it will return errors as the features or widgets have not been created yet.
2. Green Phase The actual development of feature code or the creation of a widget is done in this phase. This step is done with or without optimal writing of code; it only ensures that the features are created and the test case is successful.
3. Refactor Phase Refactoring or optimization of the code written for both testing and actual feature code needs to be done in this phase.
Steps for Implementing TDD for your application
1. Folder Structure
- Remove the test file created by the default Flutter project i.e the boilerplate test code
widget_test.dart
file in your Flutter app.
- Create a sub-folder as per the requirements for your application. If you want to implement Test Driven Development, include all logic and UI components that go with creating subfolder structures domain layer, data layer, and presentation layer otherwise moves forward with creating single files for each case.
- The test file should be created with the naming pattern
screen_name_test.dart
as shown in the image given below:
2. Finalize tests you need for your application
- Decide the type of tests you may need to add to your projects based on your requirements (data, domain and presentation or unit tests/widgets tests/integration/security tests)
3. Writing Test Cases
- Here I have taken an example at_theme_flutter pub.dev package from the atsign foundation in order to demonstrate the implementation steps in detail:
- As per the package requirement here we have decided to go with only widget testing as there is no need for any unit or security testing. So as a first step, we have created a folder for the same as per the naming conventions.
Testing a use case
Initially, we have taken a Color card widget. Here we need to write a test case in order to check whether the expected colors have been passed to the widget or not. Enter the code given below in your console:
void main() {
Widget _wrapWidgetWithMaterialApp({required Widget colorCard}) {
return TestMaterialApp(home: Builder(builder: (BuildContext context) {
SizeConfig().init(context);
return colorCard;
}));
}
import 'package:at_common_flutter/at_common_flutter.dart';
import 'package:at_theme_flutter/src/widgets/color_card.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../test_material_app.dart';
void main() {
Widget _wrapWidgetWithMaterialApp({required Widget colorCard}) {
return TestMaterialApp(home: Builder(builder: (BuildContext context) {
SizeConfig().init(context);
return colorCard;
}));
}
/// Functional test cases for Color Card Widget
group('Color Card Widget Tests:', () {
// Test Case to Check Color Card is displayed
final colorCard = ColorCard(color: Colors.orange, isSelected: true);
testWidgets("Color Card is displayed", (WidgetTester tester) async {
await tester.pumpWidget(
_wrapWidgetWithMaterialApp(colorCard: colorCard));
expect(find.byType(ColorCard), findsOneWidget);
});
});
}
Here are some important elements:
group()
=> It takes in a group of tests. It also holds the parameter description and function body.
testWidget()
=>It runs a callback in a Flutter test environment; the callback can be synchronous or asynchronous. It holds parameters like the description and function body.
tester.pumpWidget()
=> It is used to render the UI from widget given.
expect()
=> It is used to specify the expected behavior of the widget.
4. Run flutter test command
- Enter the following command into your console:
flutter test
- If your test case tends to pass all the cases then the Flutter test is considered successful, otherwise check the error, refactor the code, and re-run the command till all your test cases are passed.
Conclusion
Implementing Test-Driven Development into Flutter apps is reliable and minimizes bugs. On the other hand, it seems to be time-taking and increases the total lines of code. Using this technique to build your applications is hence purely dependent on your requirements and needs.
In this article, I have covered the basic introduction to TDD and you can experiment with the same by developing a simple UI-only app and writing test cases.
Note: Try out Mockito for writing unit tests with data and practise TDD with clean architecture.
Subscribe to my newsletter
Read articles from Gayathri Devi Srinivasan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Gayathri Devi Srinivasan
Gayathri Devi Srinivasan
Gayathri Devi Srinivasan is a highly skilled and experienced professional with a strong background in business development, strategic planning, and project management. She is a results-driven leader with a proven track record of success in both start-up and established environments. Gayathri has a unique combination of technical, business and leadership skills, which enables her to successfully manage cross-functional teams and deliver projects on time and within budget. She is a strategic thinker with a strong ability to identify and capitalize on new business opportunities. Actively contributing to some open source communities and delivering talks and hands on sessions on flutter & Dart.