Table of Contents
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:
- Create a “fake” system channel utilizing the exact unique identify
- Register a handler so the phone calls to indigenous code are intercepted
- Stub the values returned by the phone calls on our “fake” strategy channel
- 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!