Flutter MethodCallHandler Test with Mockito

Flutter Logo

MethodChannel can be used to establish a connection between platform-specific APIs and Dart code. Today, let me introduce how to use Mockito to test a MethodCallHandler implementation which is called from the platform side.

MethodChannel

MethodChannel is used to call platform-specific APIs from Dart code and vice versa. This is the technology commonly used to build a package or plugin that requires access to the APIs.

The details could be found in the official document.

Listener Callback

As an example, we will implement listener callbacks that are triggered from the platform side though MethodChannel.

SampleBridge

Let's create the SampleBridge class that communicates with platforms through MethodChannel. The basic requirement is to have MethodCallHandler to trigger a specific listener callback based on the signal from the platform.

Environment

  • Dart: 2.13.1
  • Flutter: 2.2.1

HelloListener.dart

HelloListener is the listener class that receives callbacks from the platform side. We have two functions to be called when the platform calls MethodCallHandler: helloFromTheOtherSide and goodBye.

SampleBridge.dart

SampleBridge is the main module which receives calls from the platform through MethodChannel and trigger the designated callback of HelloListener. Set handleMethodCall to MethodCallHander upon setting a HelloListener instance to its static variable.

On a side note, though we are extracting call.arguments in the same function, arguments evaluation and handling should be delegated to another module for easier and cleaner unit testing.

SampleBridge Use Case

Testing

Now, let's use Mockito to test if the right callback of HelloListener gets triggered as expected.

Adjustment for the Null Safe Dart Versions

Since Dart supported null safe, there have been changes in how to create mocks with Mockito. A simple example would be that you can no longer pass any in the arguments when setting the stub return value on functions like this: when(MockInstance.functionStub(any)).thenReturn("Stub Value!").

You can read more about it on their GitHub page.

As a solution, we have to use a module called build_runner. Before writing test cases, we will generate mock classes with this module so we can use them on the tests. Install the required modules by adding mockito and build_runner to dev_dependencies.

pubspec.yaml

method_call_handler_test.dart - for build_runner

As I mentioned earlier, pass HelloListener to the annotation “@GenerateMocks” in order to generate a HelloListener mock. By the way, you can pass multiple classes of which you would like to generate mocks since the function takes an array.

Now, run the command below to let build_runner do the job.

$ flutter pub run build_runner build

The next step is to start writing test cases importing method_call_handler_test.mocks.dart, which was created by the previous command.

method_call_handler_test.dart - with mocks

We are going to test if helloFromTheOtherSide and goodBye are called with expected arguments, and throw an error when a String argument wasn't passed. Since we are using MethodChannel, first we have to call TestWidgetsFlutterBinding.ensureInitialized(). Otherwise, an error “Null check operator used on a null value” will be raised.

Test Execution Command

$ flutter test -r expanded test/method_call_handler_test.dart

The option -r is to specify the level of the report: compact(default), expanded, or json

Result

00:00 +0: HelloListener helloFromTheOtherSide with a String argument, it should trigger onHelloFromOtherSide with the String value
00:00 +1: HelloListener helloFromTheOtherSide with no arguments passed, it should trigger onHelloFromOtherSide with correct args
00:00 +2: HelloListener goodBye with a String argument, it should trigger the onGoodBye with correct args
00:00 +3: HelloListener goodBye with no arguments passed, it should trigger onHelloFromOtherSide with correct args
00:00 +4: All tests passed!

Here, we successfully validated that the respective listener callbacks get triggered by the platform through MethodChannel.

TL;DR

You can use mockito and build_runner to test your MethodCallHandler implementation.

References

COPYRIGHT © 2023 Kohei Ando