• Flutter如何Mock MethodChannel进行单元测试
  • 发布于 2个月前
  • 857 热度
    0 评论
  • pckillers
  • 0 粉丝 50 篇博客
  •   
在做Flutter单元测试的时候,有时候我们会遇到Flutter Widget的某个方法调用了Platform的方法,这时候就需要Mock这个MethodChannel来消除依赖,否则测试用例执行到Channel的方法就会抛出异常。

1. 获取TestDefaultBinaryMessenger
在测试环境下,Flutter给我们提供了TestDefaultBinaryMessenger来拦截MethodChannel,所以我们需要先获取到它。
/// 依赖WidgetTester,需要在测试用例中获取
testWidgets('one test case', (widgetTester) async {
  final TestDefaultBinaryMessenger messenger = 
    widgetTester.binding.defaultBinaryMessenger;
});

/// 通过单例获取,写在setUp中可以在所有测试用例执行前运行
setUp(() {
    final TestDefaultBinaryMessenger messenger = 
      TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger;
}
2. Mock Flutter与Platform间的相互调用
• Flutter调用Platform方法:
• TestDefaultBinaryMessenger#setMockMethodCallHandler,第一个参数是需要拦截的MethodChannel,第二个参数是Function表示Mock调用。
• TestDefaultBinaryMessenger#allMessagesHandler,和上面类似,但这里是拦截所有的MethodChannel,并且,此项设置后,setMockMethodCallHandler将不生效。

• Platform调用Flutter方法:
• TestDefaultBinaryMessenger#handlePlatformMessage,第一个参数是MethodChannel名字,第二个参数是传给Flutter编码后的MethodCall,第三个参数是Flutter处理后结果的回调。

3. 示例
以allMessagesHandler使用举例,假如我们有这样一个MethodChannel
class PipeLinePlugin {
  PipeLinePlugin({this.pipeLineId, this.onTextureRegistered}) {
    _channel = MethodChannel('method_channel/pipeline_$pipeLineId');
    /// 堆代码 duidaima.com
    /// 调用start后,Platform会回调registerTextureId
    _channel.setMethodCallHandler((call) {
      if (call.method == 'registerTextureId' && call.arguments is Map) {
        int textureId = (call.arguments as Map)?['textureId'];
        onTextureRegistered?.call(textureId);
      }
    });
  }

  final String pipeLineId;
  final MethodChannel _channel;
  final Function(int textureId)? onTextureRegistered;

  Future<bool?> start() async {
    final bool? result = await _channel.invokeMethod('start', <String, dynamic>{'id': pipeLineId}) as bool?;
    return result;
  }
  
  Future<bool?> stop() async {
    final bool? result = await _channel.invokeMethod('stop', <String, dynamic>{'id': pipeLineId}) as bool?;
    return result;
  }
}
我们可以这样Mock它,然后我们的测试用例就能正常执行了。
const StandardMethodCodec methodCodec = StandardMethodCodec();
/// 如果channel名字是按规则生成的,可以拦截所有的MethodChannel,再从中找到你需要Mock的MethodChannel
messenger.allMessagesHandler = (String channel, MessageHandler? handler, ByteData? message) async {
  final MethodCall call = methodCodec.decodeMethodCall(message);
  if (channel.startWith('method_channel/pipeline')) {
    if (call.method == 'start') {
      /// Platform收到start后,需要回调registerTextureId
      final platformResultCall = MethodCall('registerTextureId', {'textureId': 0});
      messenger.handlePlatformMessage(channel, 
        methodCodec.encodeMethodCall(platformResultCall), null);
    }
  }

  /// Flutter的MethodCall统一返回true
  return methodCodec.encodeSuccessEnvelope(true);
}

用户评论