Flutter Testing

Flutter Testing: In flutter, testing is a very crucial part of application development's life cycle. It is a very long-lasting process in the development stage. Testing is a process of testing an application for its consistency, functionality and, usability. We are given access to various ways of testing a mobile application:

  • Unit Testing
  • Widget Testing
  • Integration Testing

Let us see the various points of differences between these testing ways on account of execution speed, maintenance, confidence and dependencies.

 UNIT TESTINGWIDGET TESTINGINTEGRATION TESTING
EXECUTION SPEED  FasterFasterSlower
MAINTENANCE  LessHigherHighest
CONFIDENCE  LessHigherHighest
DEPENDENCIES  LessHigherHighest

Unit Testing

Unit testing is a process used to check the quality and performance of an application with the help of writing some lines of code. Unit testing is beneficial as it mainly targets the logic of the application instead of the User-Interface.

Let us look at how the unit test works:

STEP 1: Adding the test dependencies.

dev_dependencies:    test: <latest_version>

STEP 2: Creating a test file.

We will create two files. For example- sample.dart and sample_test.dart.

The sample.dart file contains the class that we will test, and it will be located in the lib folder. The sample_test.dartfile contains the test, and it will be located in the test folder.

  lib/   sample.dart  test/   sample_test.dart

STEP 3: Creating a class to test.

class Sample  {  int a = 0;   void increment() => a++; }

STEP 4: Writing a test for class.

import 'package:test/test.dart'; import 'package:sample/sample.dart'; void main() {}

STEP 5: Running the test.

Now, to run the test, we will go to the run menu and select the sample_test.dart file. We can also run our tests directly from the terminal by executing the following command.

flutter test test/sample_test.dart

And for more help regarding the same, we can execute this command:

flutter test --help

Widget Testing

Widget testing is fundamentally used for testing a single widget. The main motive of widget testing is to examine the widget's user-interface such that it interacts in the similar way as expected. We need the flutter_test package for widget testing.

Let us look at how widget testing works:

STEP 1: Adding the dependency.

First of all, we will add flutter_test dependency.

dev_dependencies:   flutter_test:     sdk: flutter

STEP 2: Creating a widget to test.

In this step, we will create a widget to test. The widget will display a test and a title.

class SampleWidget extends StatelessWidget
 {  
 final String title; 
  final String message;    
 const SampleWidget( {     Key key,     @required this.title,    @required this.message,  }) : super(key: key);   
 @override  Widget build(BuildContext context)
 {   
 return MaterialApp(      title: 'Flutter Sample Widget',      home: Scaffold(         appBar: AppBar(           title: Text(title),     
    ),       
 body: Center (           child: Text(message),            ),  
  ), 
  );  
 } 
}

STEP 3: Creating a testWidgets test.

The test will make sure that SampleWidget shows a title and message.

void main()
 {   
testWidgets('SampleWidget contains a title and message', (WidgetTester tester) async 
{ 
  }
 );
 }

STEP 4: Building the widget.

We will now build SampleWidget with the help of pumpWidget() method that is provided to us by WidgetTester. We will create an instance that will give “this is title” and “this is message” as title and message respectively.

void main() 
{ 
testWidgets('SampleWidget contains a title and message', (WidgetTester tester) async 
{
 await tester.pumpWidget(SampleWidget(title: 'this is title', message: 'this is message'));
 });
 }

STEP 5: Searching the widget.

The next step is searching the widget tree for title and message Text widgets using find() method. Since we are searching for a Text widget, we will use find.text() method.

void main()
 {
 testWidgets('SampleWidget contains a title and message', (WidgetTester tester) 
async 
{   
 await tester.pumpWidget(SampleWidget(title: 'this is title', message: 'this is message'));
final titleFinder = find.text('this is title'); 
final messageFinder = find.text('this is message');
 });
 }

STEP 6: Verifying the widget.

void main()
{ 
testWidgets('SampleWidget contains a title and message', (WidgetTester tester) async
 { 
await tester.pumpWidget(SampleWidget(title: 'this is title', message: 'this is message')); 
final titleFinder = find.text('this is title'); 
final messageFinder = find.text('this is message');
expect(titleFinder, findsOneWidget); 
expect(messageFinder, findsOneWidget);
 });
 }

 For running the test, we will head over to the run menu and select the sample_test.dart option. We can run the test directly from the terminal as well by executing the commands given in the Unit Testing topic.

Integration Testing

Unit tests and widget tests are useful when dealing with single classes, widgets, and functions, but when it comes to testing of these single pieces as a whole, we need integration testing for that task.

Integration tests work as a pair: first, we deploy an instrumented application to a real device or emulator/simulator and then we “drive” the application from a separate test.

Let us look at how integration testing works:

STEP 1: Creating an application for testing.

In the first step, we will create a sample application for testing.

import 'package:flutter/material.dart';  
 void main() { runApp(MyApp()); 
} 
 class MyApp extends StatelessWidget
 { 
@override Widget build(BuildContext context) 
{ 
return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'),
 );
 } 
}

class MyHomePage extends StatefulWidget 
{ 
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override _MyHomePageState createState() => _MyHomePageState(); 
}

class _MyHomePageState extends State<MyHomePage> { int _counter = 0;

void _incrementCounter() 
{ 
setState(() 
{ _counter++; 
});
 }

@override Widget build(BuildContext context)
 { 
return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, Children: Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } }

STEP 2: Adding the dependency.

We will add flutter_driver dependency now.

dev_dependencies:   flutter_driver:     sdk: flutter   test: any

STEP 3: Creating the test files.

We will create two files for testing that will be located in the directory. By convention, the name of that directory is test_driver.

The first file will have an "instrumented" version of the application. The second file will have a test suite, which is responsible for driving the application and checking whether it works or not.

  lib/     main.dart   test_driver/     sample.dart     sample_test.dart

STEP 4: Instrumenting the application.

In this step, we will enable the flutter driver extension and then run the application.

We will now add the following code to sample.dart file.

import 'package:flutter_driver/driver_extension.dart'; import 'package:sample/main.dart' as app;   void main() {   enableFlutterDriverExtension();   app.main(); }

STEP 5: Writing the test after instrumenting.

After instrumenting, we will write tests. It has various steps:

  1. Create SerializableFinders class, a base class for Flutter Driver Finders, to locate specific widgets.
  2. Connect to the application before running it.
  3. Now we will disconnect the application using tearallDown(); this will happen asynchronously. So, this will happen when the tests are completed.

STEP 6: Running the test.

Now, running the tests are a little different in the case of Integrated testing. The processes are different for mobile and web.

For mobile, we will run the following command:

flutter drive --target=test_driver/app.dart

And for the web, it depends on which browser you are using- whether it is Chrome, Firefox or Safari.

For safari, SafariDriver is already installed.

And for Chrome, launch the web driver and write the following code, for example:

./chromedriver --port=4444

And run the below command from the root of the project.

flutter drive --target=test_driver/sample.dart --browser-name=[browser name] --release