Writing Your First Test with Flutter Integration Test
Our previous blog post compared the Appium and Flutter Integration Test based on 9 key parameters. In this blog, we will write our first end-to-end test with Flutter Integration Test
Pre-Requisites
The pre-requisites that are common for mobile test automation
- Android: Android Studio, Android Emulator Setup
- IOS: Xcode, Simulator Setup
- IDE — VS code
Setup needed specific for Flutter
All that we need is Flutter SDK and a couple of IDE plugins. Please find below the links for the same and install them.
- Flutter SDK
- Install Flutter Plugin
- Install Dart Plugin
Let's Begin
Step 1: Validate your flutter setup
Execute the command — “flutter doctor” If everything appears to be okay, you may continue; if not, follow the flutter doctor’s instructions to fix the problems.
Step 2: Sample application to automate — A TODO App
We would use a sample TODO application to write our first test. Let us clone the repository.
https://github.com/akagupta9/Flutter-To-do-List-App
Step 3: Install the dependencies
From the root folder of the project, execute the command — “flutter pub get” to install all the dependencies. This may take some time as it would download all the dependencies for the first time
Step 4: Launch the application
Let’s launch our application and understand the application’s screens and functions. Make sure your emulator or simulator is running
Right-click on lib/main.dart file and select Run without debugging.
It will launch your application in debug mode, and you can interact with it manually. The successfully launched application will look like this -
This is a typical TODO app that allows us to perform below:
- Create a TODO with a Title and Description
- Delete the TODO
- Search for TODO
- Restore Deleted TODO
In this blog, we will automate an integration test that would create a new TODO.
Step 5: Setting up the Integration Test package
Open the pubspec.yml file and add dev dependencies.
integration_test:
sdk: flutter
flutter_test:
sdk: flutter
After adding the dependency, execute the command “flutter pub get”
With this, we are all set to start authoring our test.
Step 6: Add a Test
- Create folder integration_test at the root of the project.
- We will follow the standard Page Object pattern for test automation. Each screen will have its own locators and respective methods. Test files will have one end-to-end Test.
- Let’s create a folder structure as shown in the below image
- Let us create a file todo_home_screen.dart under the screens/todo folder with the below content.
import 'package:flutter_test/flutter_test.dart';
class TodoHomeScreen {
late WidgetTester tester;
TodoHomeScreen(this.tester);
}
- Let us create a file called create_todo_test.dart under the tests folder and add the below content.
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
group('TODO :', () {
testWidgets(
'Validate TODO Creation',
(WidgetTester tester) async {},
skip: false,
timeout: const Timeout(Duration(minutes: 5)),
);
});
}
Each testWidgets represents a single test.
We need to mention binding before starting the test. This step is a must-add for the integration test.
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
Step 7: Add Locators and Actions to the Home Screen
Let's Start writing locators and actions for Todo Home Screen.
As a pre-requisite to this step, I would suggest exploring -
Now we will write locators for add todo icon, title, and description. For this you need to run lib/main.dart file in without debug mode as discussed above. It will open up your Widget Inspector. You can inspect the element, on click it will redirect you to the source code file as well as highlight the element.
Let us add identifiers for the title and description text fields as shown in the gif below.
Our locators would look like it. You can follow other strategies also for locators. But having unique keys to each locator is always good practice.
final _addTodoIconLocator = find.byType(FloatingActionButton);
final _todoTitleTextField = find.byKey(const ValueKey('todoTitleTextField'));
final _todoDescriptionTextField =
find.byKey(const ValueKey('todoDescriptionTextField'));
final _createTodoIcon = find.byIcon(LineIcons.paperPlane);
We will add one Method that will take the title and description as parameters and create a new TODO, and another method that will validate added TODO is present or not. Our TodoHomeScreen page would look
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:line_icons/line_icons.dart';
import 'package:todo_app/components/todo_tile.dart';
class TodoHomeScreen {
late WidgetTester tester;
TodoHomeScreen(this.tester);
final _addTodoIconLocator = find.byType(FloatingActionButton);
final _todoTitleTextField = find.byKey(const ValueKey('todoTitleTextField'));
final _todoDescriptionTextField = find.byKey(const ValueKey('todoDescriptionTextField'));
final _createTodoIcon = find.byIcon(LineIcons.paperPlane);
Future<void> addTodo(String title, String description) async {
await tester.tap(_addTodoIconLocator, warnIfMissed: true);
await tester.pumpAndSettle();
await tester.enterText(_todoTitleTextField, title);
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.enterText(_todoDescriptionTextField, title);
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.tap(_createTodoIcon, warnIfMissed: true);
await tester.pumpAndSettle();
}
Future<bool> isTodoPresent(String title) async {
final todoLocator = find.descendant(of: find.byType(TodoTile), matching: find.text(title));
return tester.any(todoLocator);
}
}
Now we will update our Test File which will automate adding TODO and validating it. Flutter Integration Test provides expect for validation.
Before writing the Test class, we need to launch our main application using testWidget. It's again an important step to do. If you open lib/main.dart file, you will see it internally calls TodoList() app. The same way we will call this app using testWidget. Our Test file will look like this -
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:todo_app/main.dart' as todo_app;
import '../screens/todo/todo_home_screen.dart';
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
group('TODO :', () {
testWidgets(
'Validate TODO Creation',
(WidgetTester tester) async {
// Initialing Application for Testing and waiting for it to launch
todo_app.main();
await tester.pump(const Duration(seconds: 5));
final todoHomeScreen = TodoHomeScreen(tester);
// Test Data Setup
const title = 'Automation Test TODO';
const description = 'This TODO is created by Flutter INtegration Test Package.';
// Actions to Perform
await todoHomeScreen.addTodo(title, description);
// Assertions
final isTodoCreated = await todoHomeScreen.isTodoPresent(title);
expect(isTodoCreated, true, reason: 'Expected TODO should be created and it should reflect on TODO Home Screen');
},
skip: false,
timeout: const Timeout(Duration(minutes: 5)),
);
});
}
Yeah, We are all set with First Test. Now it's time to run the test.
Execute the command:
```flutter test integration_test/tests/create_todo_test.dart```
After Successful test execution, you will get a test fail or pass message at the console.
Congratulations !! on your first successful test.
At the moment, the console displays the pass or fail status of the test. However, we are planning to include a more comprehensive report in our next blog, so keep an eye out for that.