testing method channels – Application Not Responding

Photo by Charles Forerunner on Unsplash

Introduction

As we observed on the prior entry, method channels allow us to invoke indigenous code from a Flutter app.

From the screening position of look at, system channels might seem a problem at 1st: how do we break the solid dependency on the native system in order to get our code into examination harness?

Luckily for us for us, Flutter involves some designed-in features that make testing process channels a no-brainer!

Sample aspect using a technique channel

Let us say we have a helper course in our venture that opens the corresponding application retailers so the user can update the application.

The redirection to the shop is implemented as a world-wide-web link, managed by the url_launcher flutter plugin. Under the hood, this plugin makes use of technique channels to take care of the inbound links at native platform stage.

A quite easy implementation for this class would be something like:

import 'package:url_launcher/url_launcher.dart'

course AppStoreLauncherException implements Exception 
  last String msg
  const AppStoreLauncherException(this.msg) : tremendous()


class AppStoreLauncher 
  const AppStoreLauncher() : super()

  Long run launchWebStore(bool isAndroid = correct) async 
    String url="https://apps.apple.com/..."
    if (isAndroid) 
      url="https://enjoy.google.com/retail outlet/apps/particulars?id=..."
    

    if (await canLaunch(url)) 
      await start(url)
     else 
      throw AppStoreLauncherException('Could not launch $url')
    
  

Notice that the “canLaunch()” and “start()” strategies are the types furnished by the plugin. If we want to examination this course, we’ll have to mock the values they return. Let’s see the way to do it…

Workflow

In order to “mock” a approach channel:

  1. Create a “fake” system channel utilizing the exact unique identify
  2. Register a handler so the phone calls to indigenous code are intercepted
  3. Stub the values returned by the phone calls on our “fake” strategy channel
  4. Insert some optional “sensing variables”

1. Creating a pretend channel

Instantiate a new object passing as parameter the identify of the channel we want to mock. Given that names should be exceptional, they generally look like an inversed offer identify. In the existing illustration, we ought to use the url launcher plugin title:

MethodChannel mockChannel = const MethodChannel('plugins.flutter.io/url_launcher')

2. Registering the handler

All the info exchanged concerning Flutter and the indigenous platform is despatched as a message. If we want to manage the values exchanged, we have to use the “TestDefaultBinaryMessengerBinding” mixin.

The previous class delegates the messaging attributes to a residence termed “defaultBinaryMessenger“: so that’s the item we have to use in purchase to get control more than the messages exchanged.

TestDefaultBinaryMessenger” API allow for us to mock the indigenous code invocations by applying “setMockMethodCallHandler()“:

TestDefaultBinaryMessengerBinding.occasion!.defaultBinaryMessenger.setMockMethodCallHandler(mockChannel, handler)

This strategy gets as arguments:

  • the “fake” process channel
  • a function handler that performs the precise stubbing for the values returned. It merely checks the strategy invoked (passed as parameter) and returns the price we desire for that call

So putting it all with each other:

Future? handler(MethodCall methodCall) async 
      if (methodCall.process == "canLaunch") 
        return accurate
      
      return untrue
    

TestDefaultBinaryMessengerBinding.occasion!.defaultBinaryMessenger.setMockMethodCallHandler(mockChannel, handler)

3. Stubbing values

Due to the fact the prior approach is not extremely versatile, we can wrap the code in a custom method that gets as optional parameters the return values we want to use and registers the handler with them. This way, we can regulate the “fake” channel at runtime:

void _mockChannelValues(bool canLaunch = accurate, bool start = legitimate) 
   TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
     .setMockMethodCallHandler(
        mockChannel,
        (MethodCall methodCall) async 
          if (methodCall.technique == "canLaunch") 
            return canLaunch
           else if (methodCall.method == "launch") 
            return launch
          
          return bogus
       
     )
  

4. Including optional “sensing variables”

Sensing variables” are mainly redundant properties that give you a superior perception above a snippet of code. While they’re typically included in output code, we can use them in check code as nicely, in get to come across out what is going on below the hood.

In this circumstance, we can log/sign-up every single phone invoked in our “fake” technique channel with a sensing variable. Afterwards we can check these logs to make certain every thing went as predicted and execute some assertions.

Just after all, we only have to declare a new variable:

late Checklist fakeLog

and modify it each and every time a technique is invoked:

void _mockChannelValues(bool canLaunch = real, bool start = accurate) 
   TestDefaultBinaryMessengerBinding.occasion!.defaultBinaryMessenger
     .setMockMethodCallHandler(
        mockChannel,
        (MethodCall methodCall) async 
           fakeLog.include(methodCall)
           
           //XXX: additional code here...
       
     )
  

Afterwards we can examine its contents, seeking for a distinct method or a presented variety of invocations:

anticipate(fakeLog.length, 2)
hope(
  fakeLog.map((e) => e.process), equals([
     'canLaunch',
     'launch',
  ]))

Bonus: case in point device test

Testing if the Android PlayStore is basically launched when employing our helper course:

  exam('When android retail outlet both of those canLaunch and launch are invoked', () async 
    _mockChannelValues(canLaunch: true, start: accurate)

    await launcher.launchWebStore(isAndroid: correct)

    count on(fakeLog.size, 2)
    count on(
        fakeLog.map((e) => e.system),
        equals([
          'canLaunch',
          'launch',
        ]))
  )

Troubleshotting

Given that we’re simulating the utilization of 3rd occasion libraries, native code, etc, we should make guaranteed that the Flutter screening ecosystem is correctly configured ahead of operating the assessments, otherwise we will operate into some terrible error.

So make certain you invoke:

TestWidgetsFlutterBinding.ensureInitialized()

in advance of running your exams

Sample code

As regular, resource code is obtainable here.

Write you upcoming time!

Next Post

Federal Report Reveals Inflation Could Force Households Shell Out $8,581 More In 12 Months: California Stimulus Check Soon, Other States Too Mull Relief

The impact of the pandemic carries on to linger as the document inflation has proven a vice-like grip on price ranges and refuses to budge from its higher perch.  A report by the Joint Financial Committee of the US Congress has discovered that the cost increase has been steeper than […]
Federal Report Reveals Inflation Could Force Households Shell Out ,581 More In 12 Months: California Stimulus Check Soon, Other States Too Mull Relief

You May Like