How can I mock/stub out a Flutter platform channel/plugin?
You can use setMockMethodCallHandler to register a mock handler for the underlying method channel:
https://docs.flutter.io/flutter/services/MethodChannel/setMockMethodCallHandler.html
final List<MethodCall> log = <MethodCall>[];MethodChannel channel = const MethodChannel('plugins.flutter.io/url_launcher');// Register the mock handler.channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall);});await launch("http://example.com/");expect(log, equals(<MethodCall>[new MethodCall('launch', "http://example.com/")]));// Unregister the mock handler.channel.setMockMethodCallHandler(null);
MethodChannel#setMockMethodCallHandler
is deprecated and removed as of now.
Looks like this is the way to go now:
import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart';void mockUrlLauncher() { const channel = MethodChannel('plugins.flutter.io/url_launcher'); handler(MethodCall methodCall) async { if (methodCall.method == 'yourMethod') { return 42; } return null; } TestWidgetsFlutterBinding.ensureInitialized(); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, handler);}
The details are on GitHub.
And here is a tested example for package_info
plugin for future references:
import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart';void mockPackageInfo() { const channel = MethodChannel('plugins.flutter.io/package_info'); handler(MethodCall methodCall) async { if (methodCall.method == 'getAll') { return <String, dynamic>{ 'appName': 'myapp', 'packageName': 'com.mycompany.myapp', 'version': '0.0.1', 'buildNumber': '1' }; } return null; } TestWidgetsFlutterBinding.ensureInitialized(); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, handler);}
When you create a plugin, you are automatically provided a default test:
void main() { const MethodChannel channel = MethodChannel('my_plugin'); setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { return '42'; }); }); tearDown(() { channel.setMockMethodCallHandler(null); }); test('getPlatformVersion', () async { expect(await MyPlugin.platformVersion, '42'); });}
Let me add some notes about it:
- Calling
setMockMethodCallHandler
allows you to bypass whatever the actual plugin does and return your own value. - You can differentiate methods using
methodCall.method
, which is a string of the called method name. - For plugin creators this is a way to verify the public API names, but it does not test the functionality of the API. You need to use integration tests for that.