Flutter Testing 101: How to Unit Test your Functions

A beginner's guide to setting up and executing unit tests for your Flutter functions

This article is going to be a series. In each article of the series, I will overview a test category writing about Testing in general, Unit Testing your functions, Unit Testing your service classes for Login & Sign-up, Widget testing and also Integration testing. With relatable and very understandable practical examples. I will do my best to be consistent and publish a new article to the series bi-weekly. So help me, God.

What is Testing?

Testing in Software Engineering is defined as a series of activities to check whether the actual results of our computer program/software match the expected results and to ensure that the software system is Defect free.

This phase allows the developers to go through all the possible cases that can take place when the project goes live. It also points out problems hidden in different features of the project.

Why Test your Flutter app?

We write tests to be able to have confidence in our code.

Apart from having confidence in your code, you need to also write tests to avoid system failure or crashes that may lead to an enormous loss of money, private data or even lives depending on the nature of the app you're working on. Here are a few examples of the real-life effects of software bugs.

  • In April of 1999, a software bug caused the failure of a $1.2 billion military satellite launch, the costliest accident in history

  • Also, In 2015 fighter plane F-35 fell victim to a software bug, making it unable to detect targets correctly.

Types of Tests?

Testing in Flutter falls into 3 categories:

1. Unit Testing

In a Unit Test, according to official Flutter docs, you test a single function, method, or class. The goal of a unit test is to verify the correctness of a unit of logic under a variety of conditions.

2. Widget Testing

In a Widget Test, you test a single Widget. The goal of a widget test is to verify that the widget’s UI looks and interacts as expected.

3. Integration Testing

In an Integration Test, you test 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.

Alright! it's time to get our hands dirty..

We're going to be working on Unit testing a greeting function.

Steps:

  • Create a new Flutter project -- flutter_test_tutorial

Clear, everything in the main.dart and replace it with the code below.

import 'package:flutter/material.dart';
import 'package:flutter_test_tutorial/greeting_controller.dart';

void main() {
  runApp(MyApp(GreetingController()));
}

class MyApp extends StatelessWidget {
  const MyApp(this.greetingController, {Key? key}) : super(key: key);

  final GreetingController greetingController;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Tutorial',
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Unit Testing Tutorial'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                'Greets you with respect to time of the day',
                style: TextStyle(fontSize: 18),
                textAlign: TextAlign.center,
              ),
              const SizedBox(
                height: 20,
              ),
              Text(
                'Hey, ${greetingController.greeting(DateTime.now().hour)}',
                style: const TextStyle(
                  fontSize: 32,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Create a new dart file in your lib folder and name it: greeting_controller

Inside the greeting_controller.dart, copy the code below into it.

class GreetingController {

  String greeting(hour) {
    if (hour < 12) {
      return 'Good Morning';
    }
    if (hour < 17) {
      return 'Good Afternoon';
    }
    return 'Good Evening';
  }
}

Your code should run properly now, greeting you according to your emulator /phone's system time.

We will be testing the greeting function within the GreetingController. The function is a simple function that takes in 24-hour time format using Time.Now().hour method.

If we want to manually confirm whether this function is working correctly, we could try running the code in the morning by 11 am, for it to return “Good Morning”, run it by noon and also by 6 pm to display “Good Afternoon” & “Good Evening” respectively.

Writing the tests...

Head over to the test folder on your project root and delete the widget_test.dart file.

  • Create, a new dart file in the test folder and call it greeting_controller_test.dart

Note: You should always add _test.dart after naming your file for the dart compiler to be able to recognise it as a test file.

Before we begin to write our tests, we need to install the package under dev-dependencies.

flutter pub add test --dev

Also Note: Every of your test class you create should start with void main(){}

The test() is peculiar to the test package we installed earlier.

It accepts a description and the function body

test('test description', (){});

Inside the function body, to access the greeting function in our test environment, we will create an instance of the Greeting controller class.

The expect() is a void function that accepts the arguments

void expect( dynamic actual, dynamic matcher, { String? reason, dynamic skip, bool verbose = false, String Function(dynamic, Matcher, String?, Map , bool)? formatter, })

the parameter actual, and the matcher are both dynamic functions and are both required in other for you to use the expect function.

void main(){

  final greetingController = GreetingController();


  test('greets good morning', () {

    var greet = greetingController.greeting(11);

    expect(greet, 'Good Morning');

  });

}

Now you can run the test in your terminal using the command below.

flutter test test/greeting_controller_test.dart

The test case passed yeah?

You can as well use the group() to group a series of related tests.

group("description", () { });

Here's how to go about it

group(
  "A group of Greeting tests",

  () {
    test(
      'greets good morning',
      () {
        var greet = greetingController.greeting(11);

        expect(greet, 'Good Morning');
      },
    );

    test(
      'greets good afternoon',
      () {
        var greet = greetingController.greeting(13);

        expect(greet, 'Good Afternoon');
      },
    );

    test(
      'greets good evening',
      () {
        var greet = greetingController.greeting(23);

        expect(greet, 'Good Evening');
      },
    );
  },
);

Re-run the test in your terminal using the command

flutter test test/greeting_controller_test.dart

Voila!, you've now successfully learnt how to unit test your functions. Feel free to play around with the variables.

Here’s the link to the full code on Github.

https://github.com/Syntax007/flutter_test_tutorial

Shoutout to Tamilore & Orji for reviewing my first draft 😊

Thanks for following this article till the end. Please give me a clap, if this article was beneficial to you.