Turn your manual testers into automation experts Request a DemoStart testRigor Free

Flutter Testing

What is Flutter?

Flutter is a free and open-source mobile application development framework created by Google. It was released in 2017 and has gained popularity among developers due to its ability to build high-performance, visually appealing, and native-like apps for multiple platforms, including iOS, Android, web, and desktop, using a single codebase.

Flutter uses the Dart programming language, which was also developed by Google, and provides a rich set of pre-built widgets and tools that make it easy for developers to create beautiful and functional apps. The framework uses a reactive programming model.

One of the key advantages of Flutter is its hot reload feature, which allows developers to make changes to their code and see the results instantly in the app without needing to rebuild it from scratch. This helps to speed up the development process and enables developers to iterate quickly.

Flutter also comes with a powerful set of tools for debugging and testing, including support for unit tests, integration tests, and UI tests. This helps developers to ensure that their apps are bug-free and perform well on all platforms.

Flutter framework testing

The following are the primary types of testing that can be performed using the testing framework:

Pre-Requisites

Before conducting any level of testing, there are a few pre-requisites that need to be followed for testing Flutter:
  • Add test or flutter_test dependency in pubspec.yaml file
    dev_dependencies:
    flutter_test:
    sdk: flutter
  • Create a test file with a file name ending with _test.
    For example, to test a class validatecreds.dart, we need to name the test file validatecreds_test. Dart. The test file should be placed in the test folder located at the root of your Flutter application or package.
  • Create a class to test.
    For example, to test class validatecreds.dart, we need to create and place the class file in lib folder located at the root of your Flutter application or package.
    The folder structure should be as shown below:
    login_screen/
        lib/
            validatecreds.dart
        test/
            validatecreds_test.dart
  • Write a test for the class.
    Example:
    test/
        validatecreds_test.dart

Unit Testing

Unit testing is a technique where individual units or components of a software application are tested in isolation from the rest of the system. The purpose of unit testing is to validate that each component of the application is functioning as intended and to catch any defects or bugs early in the development cycle. Following the TDD approach for writing unit tests is a great practice in Flutter.

In Flutter, app unit testing can be performed at:
  • Code level
  • Widget Level

Below is an example of unit testing at code level:

Suppose we have a simple function that adds two numbers and returns the result. Here's an example of a unit test for this function in Flutter:
dartCopy code
int addNumbers(int a, int b) {
  return a + b;
}

void main() {
  test('adds two numbers', () {
    expect(addNumbers(2, 3), equals(5));
    expect(addNumbers(-2, 3), equals(1));
    expect(addNumbers(0, 0), equals(0));
  });
}

In this example, we define an addNumbers function that takes two integer arguments and returns their sum. We then define a test case using the test function provided by the flutter_test package. The test case checks that the addNumbers function returns the expected result for three different input pairs.

Below is an example of unit testing at the widget level:

Suppose we have a simple widget that displays a greeting message to the user. Here's an example of a unit test for this widget in Flutter:
dartCopy code
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('displays greeting message', (WidgetTester tester) async {
    await tester.pumpWidget(GreetingWidget(name: 'John'));
    final greetingFinder = find.text('Hello, John!');
    expect(greetingFinder, findsOneWidget);
  });
}

class GreetingWidget extends StatelessWidget {
  final String name;

  GreetingWidget({required this.name});

  @override
  Widget build(BuildContext context) {
    return Text('Hello, $name!');
  }
}

In this example, we define a GreetingWidget class that takes a name argument and displays a greeting message with the name in a Text widget. We then define a test case using the testWidgets function provided by the flutter_test package. The test case uses the pumpWidget method to render the GreetingWidget with a specified name and then checks that the expected greeting message is displayed by searching for a Text widget with the expected text using the find.text function. The expect function then verifies that the Text widget is found exactly once using the findsOneWidget matcher.

Integration testing

Integration testing is a software testing technique that tests the interactions and interfaces between different modules or components of a software system. The purpose of integration testing is to ensure that the individual modules of the system work together as expected and to catch any defects or issues that may arise due to the interaction between different parts of the system.

In the case of Flutter apps, integration testing can be done in the following ways:
  • Using HTTP tests to perform integration testing at the API layer
  • Integration testing can be performed at the user interface level (which can also be part of E2E testing)
  • Integration testing can be performed at the database layer

Pre-requisites for integration testing

  • We need to add two packages in the dev dependencies section in the pubspec.yaml file.
    dev_dependencies:
        integration_test:
            sdk: flutter
        flutter_test:
            sdk: flutter
  • We need to group flow using 'group' keyword
  • We cannot create integration tests in the 'test' folder. We need to create a folder with the name 'integration_test' where our integration scripts will be placed.
  • For API, we need to:
    • Add http client package in dependencies section in pubspec.yml.
      dependencies:
          flutter:
              sdk: flutter
          http: 0.13.4
    • Add mockito and build_runner dependency in pubspec.yaml.
      dev_dependencies:
          flutter_test:
              sdk: flutter
          mockito: ^5.1.0
          build_runner: ^2.1.10

Example of an integration test on the API level

Suppose we want to validate a response for an API which should return a list of books. This means we need to create an API test class called fetch_books_test.dart.

Sample code test class fetch_books_test.dart:

Using @GenerateMocks([http.Client]), mockito understands the HTTP client needs to be mocked for the test class.

After creating a Mock Client, we must inject the mockClient into the fetchBooks(mockClient) method. We can then initialize Mock Client in the setUp() method, which will be used in test cases repeatedly. Anything we would like to do before the test can be specified in setUp().

We can use tearDown() method to close the mockClient once all test cases are executed. Anything we decide to do after the test, can be specified in the tearDown() method.

For more details on API integration testing, refer here.

Example of an integration test on the UI level

Suppose we want to test the interaction between Login and Home screen for the HRMS app for testing the scenario 'if username and password are correct, the user is navigated to Home Screen' then the code looks like below:

Sample integration_test:

We use 'IntegrationTestWidgetsFlutterBinding', which ensures the integration flow among all the components in the group in the test class.

Example of an integration test on the database level

Flutter has two necessary packages to work with databases:
  • sqflite - to access and manipulate SQLite database
  • firebase_database - to access and manipulate NoSQL databases from Google
Here's a sample database test:

End-to-end testing

E2E testing (also called system testing) evaluates the functionality of an entire system or application from start to finish. The goal of end-to-end testing is to ensure that all components of the system are working together as intended and that the application performs as expected under various real world scenarios.

The entire system is tested during an end-to-end test, including its integration with external systems and data sources. This type of testing is usually performed after unit testing and integration testing have been completed. The end-to-end test is the final step in the testing process before the software is released to users.

Various testing tools can be used to perform E2E testing on Flutter apps. Some examples are:

Flutter Framework

E2E testing can be implemented in the same way as explained in the UI integration testing section for Login flow.

Selenium and Appium

You can use Selenium WebDriver for Flutter web testing, and Appium for Flutter iOS and Android testing. Refer here.

testRigor

testRigor is most effective in performing E2E testing for Flutter mobile apps. There's no programming language dependency since testRigor is a codeless AI-driven tool - which means that even manual QA testers can comfortably own the process of creating, editing, and maintaining any test cases. The speed of test creation is up to 15x faster compared to Selenium or Appium, and the test maintenance issue is basically obliterated.

testRigor advantages:
  • Shared suite can be used to perform parallel testing of the app on both iOS and Android platforms.
  • Any UI change in the app can be easily tested since it supports visual testing with tools like Applitools.
  • API tests can also be performed using testRigor. Refer here.
  • Can be easily integrated with tools like JIRA, TestRail, most CI/CD tools, etc.
  • Extremely easy to implement and use.
  • Maintenance is up to 95% less compared to other e2e tools.
  • Uses plain English language for creating test cases.
  • Test execution speed is highly optimized.

Below is the test implementation for 'Login and verify an employee's timesheet for September by employee ID' for the HRMS app.

enter “mgeethan@gmail.com” into “Username”
enter “password” into “Password”
click on “Login”
check that page contains “Welcome to HRMS”
click on “Timesheet”
check that page contains “Employee Details”
check that table “Employee Details” at row “2” and column “Action” contains link “12451”
click on “12451”
check that page contains “Month: September Total hours worked: 160”

testRigor provides a very effective way of interacting with forms and tables. You can refer to cells in a table with the intersection of rows and columns.

Conclusion

The article above provides an overview of the types of technologies and tools that can be used to create a robust and efficient testing framework. Implementing such a framework can help maximize ROI, ensure high-quality deliverables, and provide the best possible user experience for customers.