What tests should a QA Engineer write for the Flutter?

AppVesto LLC
6 min readDec 7, 2020

Hello to all testers who want to understand the automation in Flutter. My name is Tanya, and I am a QA Engineer at Appvesto. In this article, I want to share the tests that I write and understand each step to write them in detail. Well, let’s go.

Widget testing

What is a test widget? Widget tests test a single widget and aim to make sure that the interface looks and interacts with the user as planned. Testing a widget includes several classes and requires a test environment that provides the widget’s life cycle’s appropriate context.

To test the widget classes, you will need the flutter_test package, which contains several additional tools such as
WidgetTester, which allows you to create widgets and interact with them in a test environment;
The testWidgets () function, which automatically creates a new WidgetTester for each test example and is used instead of the usual test function ();
The Finder classes allow you to search for widgets in the test environment;
Matcher constants for specific widgets help to check if the Finder detects a widget or several widgets in the test environment.

Now let’s consider how to write these same tests and what we will get as a result:

1. First, we will add the package flutter_test to the section dev_dependencies in pubspec.yaml file. If you create a new Flutter project, this package is already added.

dev_dependencies:
flutter_test:
sdk: flutter

2. Let’s create a widget that we will test. I, for example, create a form with one field and a button. The expected result will be the display of the entered word on the next page.

void main() => runApp( MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
var _textController = TextEditingController();

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: ListView(
children: <Widget>[
ListTile(
title: TextFormField(
key: Key('textField'),
controller: _textController,
),
),
ListTile(
title: RaisedButton(
key: Key("button"),
child: Text("Next"),
onPressed: () {
var route = MaterialPageRoute(
builder: (BuildContext context) =>
NextPage(value: _textController.text),
);
Navigator.of(context).push(route);
},
),
),
],
),
);
}
}

class NextPage extends StatefulWidget {
final String value;

NextPage({Key key, this.value}) : super(key: key);
@override
_NextPageState createState() => _NextPageState();
}

class _NextPageState extends State<NextPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Next Page"),
),
body: Text("${widget.value}"),
);
}
}

And here is the result:

3. Now we can start writing the test. Open the test folder, or create if you do not have one. The folder stores the file widget_test.dart, in which we will create our tests. To do this, we need the testWidgets() function, which is provided by the flutter_test package:

import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Test form', (WidgetTester tester) async {
// CODE.
});
}
}

4. Then, using pumpWidget() method, we display the widget we created:

import 'package:flutter_test/flutter_test.dart';
import 'package:test_article/main.dart';

void main() {
testWidgets('Test form', (WidgetTester tester) async {

await tester.pumpWidget(MyApp());

});
}

With the help of a widget, in the test environment, we search the widget tree using the Finder. This allows you to check the correctness of the widgets. To do this, use the find() method provided by the flutter_test package to create Finders. I usually use the search type — find.byType() and the key — find.byKey(), you can also use the text search — find.text(), the icon search — find.byIcon().

import 'package:flutter_test/flutter_test.dart';
import 'package:test_article/main.dart';

void main() {
testWidgets('Test form', (WidgetTester tester) async {

await tester.pumpWidget(MyApp());

final textfield = find.byType(TextFormField);
final button = find.byType(RaisedButton);
});
}

Make sure that the field widget and buttons are displayed on the screen with Matcher. This is a way to check the expected result:

import 'package:flutter_test/flutter_test.dart';
import 'package:test_article/main.dart';

void main() {

testWidgets('Counter increments ', (WidgetTester tester) async {

await tester.pumpWidget(MyApp());

final textfield = find.byType(TextFormField);
final button = find.byType(RaisedButton);

expect (textfield,findsOneWidget);
expect(button, findsOneWidget);

});
}

Let’s see how we can change the state of the widget and interact with it. Here are a couple of methods to change the state:
tap — send the widget a click;
longPress — a long press;
fling — swipe/ swipe;
drag — transfer;
enterText — input text.
I want to fill in the field and click on the button. And here’s the result:

void main() {
testWidgets('Counter increments ', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
await tester.enterText(find.byKey(Key("textField")),'test');
await tester.tap(find.byType(RaisedButton));
});
}

And finally the test run. I launch from the console with a command where I specify a file name:
$ flutter test/widget_test.dart
If all your tests passed, you will get: All tests passed!

Integration testing
Now I suggest that you understand what integration testing is. If you ask me, it is more interesting and you can use it to test the entire application. I can say that it, in some case, can replace you manual testing.
This is a test, which runs the entire application on the emulator, or on a real device, and gives the result of passing the test.

Now let’s find out how to write such tests:
1. Let’s create a test widget. I use the same widget as the test widget.
2. For integration tests we use the package test_driver. We add it to the section dev_dependencies, in pubspec.yaml file.

dev_dependencies:
flutter_driver:
sdk: flutter
test: any

3. Now we need two files: main.dart, main_test.dart which are in the same test_driver directory (to be created).
The first file contains the “equipped” version of the application. The toolkit allows you to “manage” the application.
The second file contains a set of tests that launches the application and checks if it works properly. The name of the test file must match the name of the file containing the toolbox application with the addition of _test at the end.
4. We add the code to the main.dart file:

import 'package:flutter_driver/driver_extension.dart';
import 'package:test_article/main.dart' as app;

void main() {

enableFlutterDriverExtension();

app.main();
}

5. And now we can start to write our tests. For this we open our second file main_test.dart.
With the setUpAll() command, we connect to the application before running the tests. To disconnect from the application we use tearDownAll().

import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void main() {
group('Counter App', () {

final textfield = find.byValueKey('textField');
final button = find.byValueKey('button');
final nextPage = find.byValueKey('nextPage');

FlutterDriver driver;

// Connect to Flutter driver before performing the test
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
// End of the driver connection after tests


test('fill the field', () async {

await driver.waitFor(textfield);
//Задержка
await waitTime();

await driver.tap(textfield);
await waitTime();

await driver.enterText('Test');
await waitTime();

await driver.tap(button);
await waitTime();
});

}, timeout: Timeout(Duration(seconds: 10)));
}
//Delays part of asynchronous function

void waitTime([int seconds = 1]) async {
await Future.delayed(Duration(seconds: seconds));
}

6. Let’s run our test. Run the emulator/simulator first. Then use the command and watch how the tests are done: $ flutter drive — target=test_driver/main.dart.

Conclusion
Well, that’s it. If you haven’t written auto tests before, I want to congratulate you, you have made the first step. Or if you have already faced them, you may have learned something new for yourself. We have considered the introduction to writing auto tests on Flutter for testers. Simple tests for a form with a field and a button that you can complicate and do, for example, for an authorization form. More information on tests you can always find on the official Flutter website.

I wish everyone an easy test writing and finding serious bugs.

--

--

AppVesto LLC

We are a team of rock-star developers, which provides fully-flexible and powerful products 💥.