While both unit and integration tests are integral facets of a robust testing approach in the Flutter test regime, they have significant differences in scope and function.
Unit tests in Flutter focus on the granular level, dealing with particular, isolated functions, methods, or widgets, validating that each part of the app behaves as expected independently. While unit tests help quickly identify and solve specific code errors, they don't provide a broader picture of the app's holistic performance.
In contrast, integration testing in Flutter tests the application as a whole. It checks the synchronization, communication, and data exchange between different app components, ensuring that they work together seamlessly when united. This process is indispensable for high-level verification of the system's composite function.
By writing integration tests, bugs that occur due to interactions between components can be caught and fixed, helping achieve optimal app performance.
To illustrate how to write integration tests process in Flutter, let's develop a simple app, such as a "Tasks completion" app. This widget-based app will allow users to mark tasks as completed by tapping them.
Here's the code:
1 import 'package:flutter/material.dart'; 2 3 void main() => runApp(const MyApp()); 4 5 class MyApp extends StatelessWidget { 6 const MyApp({super.key}); 7 8 @override 9 Widget build(BuildContext context) { 10 return const MaterialApp( 11 title: 'Tasks Completion App', 12 home: MyHomePage(), 13 ); 14 } 15 } 16 17 class MyHomePage extends StatefulWidget { 18 const MyHomePage({super.key}); 19 20 @override 21 State<MyHomePage> createState() => _MyHomePageState(); 22 } 23 24 class _MyHomePageState extends State<MyHomePage> { 25 List<String> _tasks = ['Task 1', 'Task 2', 'Task 3']; 26 27 @override 28 Widget build(BuildContext context) { 29 return Scaffold( 30 appBar: AppBar( 31 title: const Text('Tasks Completion App'), 32 ), 33 body: ListView.builder( 34 itemCount: _tasks.length, 35 itemBuilder: (context, index) { 36 return ListTile( 37 // Providing a Key to the ListTile. This allows identifying the 38 // widget from inside the test suite. 39 key: Key('task_${_tasks[index]}'), 40 title: Text(_tasks[index]), 41 trailing: const Icon(Icons.check_circle, color: Colors.green), 42 onTap: () { 43 setState(() { 44 _tasks.removeAt(index); 45 }); 46 }, 47 ); 48 }, 49 ), 50 ); 51 } 52 } 53
In our example, the app lists three tasks and users can tap to mark them as complete, which would subsequently remove them from the list.
Before writing Flutter integration tests, we must ensure our environment is set up to perform them. The first step is to include the integration_test package in our project's dependencies.
To do so, we add the integration_test and flutter_test packages to the dev_dependencies section of our app's pubspec.yaml file. The Flutter SDK is specified as the location of the package.
Here's an example:
1 dev_dependencies: 2 integration_test: 3 sdk: flutter 4 flutter_test: 5 sdk: flutter 6
Upon adding the dependencies and saving the pubspec.yaml file, Flutter updates and downloads the necessary testing packages.
When maintaining order in our tests, we typically create a separate directory to hold our integration test files. For instance, we can create a directory named integration_test at the root level of the project.
In our case, we'll create an empty app_test.dart file for our Flutter integration testing script inside our integration_test directory. Here's how our project structure now looks:
1 tasks_completion_app/ 2 lib/ 3 main.dart 4 integration_test/ 5 app_test.dart 6
Upon creating the test file, it's time to kickstart our Flutter test journey and write our first integration test. For this, an instance of the IntegrationTestWidgetsFlutterBinding class needs to be initialized. This plays a crucial role in executing tests on an actual device.
We'll utilize the WidgetTester class, which provides various ways to interact with our widgets.
The testWidgets function will assist in verifying the specific behaviour of the app's interface and interactions.
Before we start writing Flutter integration testing scenarios, initializing the IntegrationTestWidgetsFlutterBinding class is crucial. This class is a singleton service that runs our tests on physical devices.
Here's how we do that:
1 void main() { 2 IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 3 ... 4 } 5
This initialization ensures the test binding is correctly set up before we commence our tests.
WidgetTester proves to be a valuable tool in our interaction with widgets. It allows us to simulate user interaction, replay recorded events, pump frames for rendering, and inspect the current widget tree.
Let's see it in the context of our "Tasks Completion" app. We will:
After writing integration tests, it's time to run them and see how your app behaves. The process differs depending on whether you are testing against a mobile platform or the web.
If you want to test your Flutter integration scenarios on a real iOS/Android device, connect it to your machine first. Once connected, you may run integration tests with the following command from the root of your project:
1 flutter test integration_test/app_test.dart 2
This command runs the application, followed by the integration test on the target device.
In case you wish to run all integration tests found in a directory, specify the directory as shown below:
1 flutter test integration_test 2
The command above will run all the test files in the integration_test directory.
Getting started with testing in a web browser requires downloading ChromeDriver. Once done, we create a new directory named test_driver comprising a new file named integration_test.dart.
1 import 'package:integration_test/integration_test_driver.dart'; 2 3 Future<void> main() => integrationDriver(); 4
Launch chromedriver as specified:
1 chromedriver --port=4444 2
Finally, from the root of your project, issue the following command:
1 flutter drive \ 2 --driver=test_driver/integration_test.dart \ 3 --target=integration_test/app_test.dart \ 4 -d chrome 5
For a headless testing experience, you can use the web server as the target device identifier:
1 flutter drive \ 2 --driver=test_driver/integration_test.dart \ 3 --target=integration_test/app_test.dart \ 4 -d web-server 5
Integration testing in Flutter is not just a 'good-to-have', but rather a 'must-have'. It safeguards against unforeseen issues that may arise when different app components interact. Its crucial role in maintaining a high level of reliability makes it an indispensable part of our Flutter development workflow.
This comprehensive guide was designed to give you a practical and hands-on understanding of the process and the benefits of integrating testing in the Flutter app. While we've covered a significant chunk of this topic, there is always more to learn and explore in the ever-evolving world of Flutter and app development. Keep coding, keep testing, and remember: Your endeavour for perfection in your app's behaviour is directly proportional to the rigour of your testing strategies.
Thank you for accompanying me on this testing journey, and looking forward to exploring more Flutter territories with you!
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.