0% found this document useful (0 votes)
6 views

Unit- Widget and Integration Testing

The document outlines the concepts and procedures for Unit, Widget, and Integration testing in Flutter applications. It covers the definitions, trade-offs, and advantages of different testing types, as well as practical instructions for writing and running tests, including the use of mocking with the Mockito package. Additionally, it provides examples of testing Bloc and widget functionalities, emphasizing the importance of automated testing for app reliability.

Uploaded by

Yabtsega
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

Unit- Widget and Integration Testing

The document outlines the concepts and procedures for Unit, Widget, and Integration testing in Flutter applications. It covers the definitions, trade-offs, and advantages of different testing types, as well as practical instructions for writing and running tests, including the use of mocking with the Mockito package. Additionally, it provides examples of testing Bloc and widget functionalities, emphasizing the importance of automated testing for app reliability.

Uploaded by

Yabtsega
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 66

Unit, Widget and Integration

Testing
Learning Outcomes
After completing this session you should be able to

● Define what Unit, Widget/Component, and Integration tests are

● Explain the tradeoffs of using Unit, Widget and Integration tests


● Write and run Unit Tests in Flutter

● Write and run Unit tests for Bloc

● Explain the advantages of Mocking

● Use Mockito package to mock dependencies


Learning Outcomes
After completing this session you should be able to

● Write and run widget tests

● Write and run Integration tests


Testing Flutter apps
Automated testing falls into a few categories:

● A unit test tests a single function, method, or class


● A widget/component test tests a single widget
● An integration test tests a complete app or a large part of an app
Test Tradeoffs

Unit Widget Integration


Confidence Low Higher Highest
Maintenance cost Low Higher Highest
Dependencies Few More Most

Execution speed Quick Quick Slow


Test Proportion
A well-tested app has many unit and widget tests, tracked by code coverage,
plus enough integration tests to cover all the important use cases
Unit Test
The goal of a unit test is to verify the correctness of a unit of logic under a
variety of conditions

External dependencies of the unit under test are generally mocked out

Unit tests generally don’t read from or write to disk, render to screen, or
receive user actions from outside the process running the test

For more information regarding unit tests, you can view the following recipes
or run

flutter test --help


Widget Test
A widget test (in other UI frameworks referred to as component test) tests a
single widget

The goal of a widget test is to verify that the widget’s UI looks and interacts
as expected

Testing a widget involves multiple classes and requires a test environment


that provides the appropriate widget lifecycle context
Widget Test
The Widget being tested should be able to receive and respond to user actions
and events, perform layout, and instantiate child widgets

A widget test is more comprehensive than a unit test

Like a unit test, a widget test’s environment is replaced with an


implementation much simpler than a full-blown UI system
Integration Test
An integration test tests a complete app or a large part of an app

The goal of an integration test is to verify that all the widgets and services
being tested work together as expected

Furthermore, you can use integration tests to verify your app’s


performance
Integration Test
Generally, an integration test runs on a real device or an OS emulator,
such as iOS Simulator or Android Emulator

The app under test is typically isolated from the test driver code to avoid
skewing the results
General Testing Procedure in Flutter
To perform tests in flutter you can follow the following steps
● Add the required dependencies in the pubspec.yaml
● Create a test files (should have _test.dart suffix)
● Identify the classes, functions, widgets or flutter application that you want
to test
● Write tests
● Combine multiple tests in a group if needed
● Run the tests
General Directory Structure for Testing
Unit Testing - Add the test dependency
The test package provides the core functionality for writing tests in Dart
Add the dependency shown below in your pubspec.yaml file or run the
command:
flutter pub add test --dev
Unit Testing - Create a test file
Let us assume we want to test the following class - a unit

class Counter {
int value = 0;
void increment() => value++;
void decrement() => value--;
}
Test Directory and File Naming
In general, unit test files should reside inside a
test folder located at the root of your Flutter
application or package

Test files should always end with _test.dart,


this is the convention used by the test runner
when searching for tests
Writing Unit Tests
Tests are defined using the top-level test function, and you can check if the
results are correct by using the top-level expect function that come from
the test package
Writing Unit Tests
Inside the counter_test.dart file add the test code
import 'package:test/test.dart';
import 'package:counter_app/counter.dart';
void main() {
test('Counter value should be incremented', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
}
Grouping Multiple Tests
If you have several related
tests you can combine them
using the group function
provided by the test
package
Run tests using
You can run tests using the tools provided by IDEs or from terminal using
flutter test <file-path> command as shown below

flutter test test/counter_test.dart

For more options regarding unit tests, you can execute this command:

flutter test --help


Unit Testing Bloc
Add bloc_test and test dependency

flutter pub add bloc_test --dev

flutter pub add test --dev


Bloc To Test
abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}


class CounterDecrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {


CounterBloc() : super(0) {
on<CounterIncrementPressed>(
(event, emit) => emit(state + 1));
on<CounterDecrementPressed>(
(event, emit) => emit(state + 1));
}
}
Bloc Test
We want to test the following three states of the bloc

● Nothing is emitted with no CounterEvent


● When CounterIncrementPressed event is added the bloc should
emit 1
● When CounterDecrementPressed event is added the bloc should
emit -1
Bloc Test (counter_bloc_test.dart)
group('CounterBloc', () {
blocTest(
'emits [] when nothing is added',
build: () => CounterBloc(),
expect: () => [],
);

blocTest(
'emits [1] when CounterIncrementPressed is added',
build: () => CounterBloc(),
act: (bloc) => bloc.add(CounterIncrementPressed()),
expect: () => [1],
);
});
Bloc Test (counter_bloc_test.dart)
blocTest<CounterBloc, int>(
'emits [] when no event is added',
build: () => CounterBloc(),
expect: () => const [],
);
Bloc Test (counter_bloc_test.dart)

blocTest<CounterBloc, int>(
'emit [1] when CounterIncrementPressed is added',
build: () => CounterBloc(),
act: (bloc) => bloc.add(CounterIncrementPressed()),
expect: () => [1],
);
Bloc Test (counter_bloc_test.dart)

blocTest<CounterBloc, int>(
'emit [-1] when CounterDecrementPressed() is added',
build: () => CounterBloc(),
act: (bloc) => bloc.add(CounterDecrementPressed()),
expect: () => [-1],
);

https://pub.dev/packages/bloc_test/example
Mocking Dependencies
In most cases classes depend on other classes that fetch data from live web
services or databases, for instance

This is inconvenient in case of unit testing for the following reasons

● Calling live services or databases slows down test execution (Speed)


● A passing test might start failing if a web service or database returns
unexpected results (Predictability/Reproducibility)
● It is difficult to test all possible success and failure scenarios by
using a live web service or database (Control)
Mocking Dependencies
For the cons mentioned, rather than relying on a live web service or database,
it is better to “mock” these dependencies

Mocks allow emulating a live web service or database and return deterministic
results depending on preconfigured setup/situation

You can mock dependencies by creating an alternative implementation of a


class or you can use packages such as the Mockito
Mocking Example
Add the mockito and the build_runner package dependencies in your
pubspec.yaml file or run the following commands

flutter pub add mockito --dev

flutter pub add build_runner --dev

The build_runner package is used for mock code generation


Function to test (fetch_album.dart)
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'album.dart';

Future<Album> fetchAlbum(http.Client client) async {


// ...
}

You can get the Album class from


https://flutter.dev/docs/cookbook/networking/fetch-data
Function to test (fetch_album.dart)
Future<Album> fetchAlbum(http.Client client) async {
final response = await
client.get(Uri.parse('https://jsonplaceholder.typicode.com/album
s/1'));
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
You can get the Album class from
} https://flutter.dev/docs/cookbook/networking/fetch-data
Mocking http.Client (fetch_album_test.dart)
import 'package:http/http.dart' as http;
import 'package:mockito/annotations.dart';

@GenerateMocks([http.Client])
void main() {
}

Run the following command to generate the mock file

flutter pub run build_runner build


Mocking http.Client
The last file shown below is the generated mock file
fetch_album_test.mocks.dart
Testing fetchAlbum(http.Client) function
The fetchAlbum() function does one of two things:

● Returns an Album if the http call succeeds

● Throws an Exception if the http call fails

We want to test these two conditions


Testing fetchAlbum(http.Client) function
Use the MockClient class to return an “Ok” response for the success test,
and an error response for the unsuccessful test

Test these conditions using the when() and thenAnswer() functions


provided by Mockito
Testing fetchAlbum(http.Client) function
Testing fetchAlbum(http.Client) function
test('returns an Album if the http call completes successfully', () async
{

final client = MockClient();


when(client
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')))

.thenAnswer((_) async =>

http.Response('{"userId": 1, "id": 2, "title": "mock"}', 200));

expect(await fetchAlbum(client), isA<Album>());

});
Testing fetchAlbum(http.Client) function
test('throws an exception if the http call completes with an error',
() {
final client = MockClient();

when(client
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')))
.thenAnswer((_) async => http.Response('Not Found', 404));

expect(fetchAlbum(client), throwsException);
});
});

Run the test using flutter test test/fetch_album_test.dart


Testing Widgets
The flutter_test package provides the following tools for testing widgets
● WidgetTester
● testWidgets()
● Finder
● Matcher
Testing Widgets
The flutter_test package provides the following tools for testing widgets
● The WidgetTester allows building and interacting with widgets in a
test environment
● The testWidgets() function automatically creates a new
WidgetTester for each test case, and is used in place of the normal
test() function
Testing Widgets
The flutter_test package provides the following tools for testing widgets
● The Finder classes allow searching for widgets in the test
environment
● Widget-specific Matcher constants help verify whether a Finder
locates a widget or multiple widgets in the test environment
Add flutter_test Dependency
Add the flutter_test dependency in your pubspec.yaml file or run the
following command

flutter pub add flutter_test --dev


A widget to test
A widget to test
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
),
);
}
Create a testWidgets test
Put the following code in the test file (my_widget_test.dart)

void main() {
testWidgets('MyWidget has a title and message',
(WidgetTester tester) async {
// Test code goes here.
});
}
Build the widget using the WidgetTester
void main() {

testWidgets('MyWidget has a title and message',


(WidgetTester tester) async {

// Create the widget by telling the tester to build it.

await tester.pumpWidget(

const MyWidget(title: 'Test Title', message: 'Test


Message'));

});

}
Build the widget using the WidgetTester
Variations of tester.pumpWidget() function

tester.pump(Duration duration)

tester.pumpAndSettle()

These methods provide fine-grained control over the build lifecycle


Search for widgets using a Finder
To verify that the widgets are being displayed correctly we need to find them

We are looking for for Text widgets, therefore we should use the
find.text() method
Search for widgets using a Finder
void main() {
testWidgets('MyWidget displays title and message',
(WidgetTester tester) async {
await tester
.pumpWidget(MyWidget(title: 'Test 123', message: 'Message 123'));

final tittleFinder = find.text('Test 123');


final messageFinder = find.text('Message 123');

expect(tittleFinder, findsOneWidget);
expect(messageFinder, findsWidgets);
});
}
Other Finders
To find a widget with a specific Key
testWidgets('finds a widget using a Key', (WidgetTester tester) async {
// Define the test key.
const testKey = Key('K');

// Build a MaterialApp with the testKey.


await tester.pumpWidget(MaterialApp(key: testKey, home: Container()));

// Find the MaterialApp widget using the testKey.


expect(find.byKey(testKey), findsOneWidget);
});
Other Finders
Find a specific widget instance
testWidgets('finds a specific instance', (WidgetTester tester)
async {
const childWidget = Padding(padding: EdgeInsets.zero);

// Provide the childWidget to the Container.


await tester.pumpWidget(Container(child: childWidget));

// Search for the childWidget in the tree and verify it exists.


expect(find.byWidget(childWidget), findsOneWidget);
});
Verify the widget using a Matcher
void main() {
testWidgets('MyWidget has a title and message', (WidgetTester
tester) async {
await tester.pumpWidget(const MyWidget(title: 'Test Title',
message: 'Test Message'));
final titleFinder = find.text('Test Title');
final messageFinder = find.text('Test Message');

expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}
Other Matchers
findsNothing

Verifies that no widgets are found

findsWidgets

Verifies that one or more widgets are found


Other Matchers
findsNWidgets

Verifies that a specific number of widgets are found

matchesGoldenFile

Verifies that a widget’s rendering matches a particular bitmap image


Tap, drag, and enter text
Many widgets not only display information, but also respond to user
interaction
This includes buttons that can be tapped, and TextField for entering text
To test these interactions, you can use the following methods from the
WidgetTester class
● enterText()
● tap()
● drag()

https://flutter.dev/docs/cookbook/testing/widget/tap-drag
Tap, drag, and enter text
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());

// Verify that our counter starts at 0.


expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);

// Tap the '+' icon and trigger a frame.


await tester.tap(find.byIcon(Icons.add));
await tester.pump();

// Verify that our counter has incremented.


expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
Integration Testing
A widget test verifies the behavior of Flutter widgets without running the
app itself

An integration test (also called end-to-end testing or GUI testing) runs the
full app
Integration Testing
Hosts and targets

The machine you are using for developing the application, writing and
running tests is called Host Machine

The emulator, the mobile device or the browser which run the flutter
application is called Target Device
Integration Testing
flutter_driver

The flutter_driver package runs integration tests written in Dart on a


target device and reports the results to the host

Tests written with flutter_driver run from the host and drive the
app running on a real or virtual device

The flutter drive command is used to run tests written with this package
Integration Testing
integration_test
Tests written with the integration_test package can:
● Run directly on the target device, allowing you to test on multiple Android
or iOS devices using Firebase Test Lab.
● Run using flutter_driver.
● Use flutter_test APIs, making integration tests more like writing widget
tests.
Integration Testing
In your project, create a new directory integration_test/ with a new file,
<name>_test.dart

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets("failing test example", (WidgetTester tester)


async {
expect(2 + 2, equals(5));
});
}
Integration Testing
To run tests with flutter drive, create a new directory containing a new file,
test_driver/integration_test.dart

import 'package:integration_test/integration_test_driver.dart';
Future<void> main() => integrationDriver();

https://github.com/flutter/flutter/tree/master/packages/integration_test#usage
Example Unit, Widget, and Integration Test
Check the following example to see all three tests together
https://codelabs.developers.google.com/codelabs/flutter-app-testing/
References
https://flutter.dev/docs/cookbook (Testing section)

Bloc State Management Library

bloc_test | Dart Package

How to test a Flutter app


Reading Assignment
Testing Riverpod

https://riverpod.dev/docs/cookbooks/testing

You might also like