Mocking callbacks in Dart/Flutter unit tests

Mocking callbacks in Dart/Flutter unit tests

This article is a guide on how to mock callback functions and verify that the callback functions are called in Dart/Flutter unit tests.

The article is divided into:

  1. Introduction
  2. Mocking The Callback
  3. Testing The Callback

1. Introduction

Let's say we have a function readDate with the definition below:

void readData(void Function() onDataReadComplete) {
    print('Reading data');
    onDataReadComplete();
}

The function takes a callback onDataReadComplete as a lone positional parameter and it calls onDataReadComplete after it prints ('Reading data').

Our task here is to write a unit test to confirm that onDataReadComplete is run whenever readData runs.

In order to do this we need to mock onDataReadComplete and confirm that it is actually called.

2. Mocking The Callback

In other to mock the callback, we will use the mock library mockito.

The first step is to add the mockito dependecy to our project by including it in the pubspec.yaml file. We will also add the test library to the pubspec.yaml file.

dev_dependencies:
  mockito:
  test:

Mockito has two ways to generate mock objects:

  1. Code Generation
  2. Manual Mock Implementation

For this article, we will use the manual mock implementation for simplicity.

A class can be mocked manually by creating a class that extends Mock and implements the class to be mocked.

Below is a sample from the documentation:

class MockHttpServer extends Mock implements HttpServer {}

This means we need a class to represent our function so our mock class can implement it.

We will make use of Dart's Callable Classes.

To allow an instance of your Dart class to be called like a function, implement the call() method.

- Callable Classes

This means we can represent our void Function() onDataReadComplete function as this below:

abstract class OnDataReadComplete {
    void call();
}

The class is an abstract class since we won't be providing an implementation of the call method .

The call method must have the same definition as the function to be mocked. This is why it is a void method.

Now, we can create our mock class like this below:

class MockOnDataReadComplete extends Mock implements OnDataReadComplete {}

We can now, use MockOnDataReadComplete in our test.

3. Testing The Callback

We will follow the following steps in testing the callback function:

  1. Create the mock object mockOnDataReadComplete using MockOnDataReadComplete.
  2. Run the readData function with mockOnDataReadComplete passed as a parameter.
  3. Use the verify function to confirm that mockOnDataReadComplete() is called once. Note that you need to call the function like this mockOnDataReadComplete() and not just pass the variable mockOnDataReadComplete to the verify function
test('onDataReadComplete gets called whenever readData runs', () {
    //Creates the mock object
    final mockOnDataReadComplete = MockOnDataReadComplete();

    //Runs the function
    readData(mockOnDataReadComplete);

    //Confirm that the mock object is called
    verify(mockOnDataReadComplete()).called(1);
  });

Here is the full test file:

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

void readData(void Function() onDataReadComplete) {
  print('Reading data');
  onDataReadComplete();
}

abstract class OnDataReadComplete {
  void call();
}

class MockOnDataReadComplete extends Mock implements OnDataReadComplete {}

void main() {
  test('onDataReadComplete gets called whenever readData runs', () {
    //Creates the mock object
    final mockOnDataReadComplete = MockOnDataReadComplete();

    //Runs the function
    readData(mockOnDataReadComplete);

    //Confirm that the mock object is called
    verify(mockOnDataReadComplete()).called(1);
  });
}

And now we run our tests and it passes!

The complete project can be found on Github.