如何mock/stub 出一个 Flutter 平台 channel/plugin?
How can I mock/stub out a Flutter platform channel/plugin?
我读了 introduction to platform-specific plugins/channels on the Flutter website and I browsed some simple examples of a plugin, like url_launcher
:
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/services.dart';
const _channel = const MethodChannel('plugins.flutter.io/url_launcher');
/// Parses the specified URL string and delegates handling of it to the
/// underlying platform.
///
/// The returned future completes with a [PlatformException] on invalid URLs and
/// schemes which cannot be handled, that is when [canLaunch] would complete
/// with false.
Future<Null> launch(String urlString) {
return _channel.invokeMethod(
'launch',
urlString,
);
}
在小部件测试或集成测试中,如何模拟或存根通道以便我不必依赖真实设备(运行 Android 或 iOS)比如说,实际启动 URL?
您可以使用 setMockMethodCallHandler 为底层方法通道注册模拟处理程序:
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);
当您创建插件时,系统会自动为您提供一个默认测试:
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');
});
}
让我添加一些注释:
- 调用
setMockMethodCallHandler
允许您绕过实际插件所做的任何事情并 return 您自己的值。
- 可以通过
methodCall.method
来区分方法,是调用方法名的字符串
- 对于插件创建者来说,这是一种验证 public API 名称的方法,但它不会测试 API 的功能。您需要为此使用集成测试。
MethodChannel#setMockMethodCallHandler
现已弃用并删除。
看起来这是现在要走的路:
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);
}
详情在GitHub.
下面是 package_info
插件的测试示例,供以后参考:
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);
}
我读了 introduction to platform-specific plugins/channels on the Flutter website and I browsed some simple examples of a plugin, like url_launcher
:
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/services.dart';
const _channel = const MethodChannel('plugins.flutter.io/url_launcher');
/// Parses the specified URL string and delegates handling of it to the
/// underlying platform.
///
/// The returned future completes with a [PlatformException] on invalid URLs and
/// schemes which cannot be handled, that is when [canLaunch] would complete
/// with false.
Future<Null> launch(String urlString) {
return _channel.invokeMethod(
'launch',
urlString,
);
}
在小部件测试或集成测试中,如何模拟或存根通道以便我不必依赖真实设备(运行 Android 或 iOS)比如说,实际启动 URL?
您可以使用 setMockMethodCallHandler 为底层方法通道注册模拟处理程序:
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);
当您创建插件时,系统会自动为您提供一个默认测试:
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');
});
}
让我添加一些注释:
- 调用
setMockMethodCallHandler
允许您绕过实际插件所做的任何事情并 return 您自己的值。 - 可以通过
methodCall.method
来区分方法,是调用方法名的字符串 - 对于插件创建者来说,这是一种验证 public API 名称的方法,但它不会测试 API 的功能。您需要为此使用集成测试。
MethodChannel#setMockMethodCallHandler
现已弃用并删除。
看起来这是现在要走的路:
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);
}
详情在GitHub.
下面是 package_info
插件的测试示例,供以后参考:
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);
}