【问题标题】:How can I mock/stub out a Flutter platform channel/plugin?如何模拟/存根 Flutter 平台通道/插件?
【发布时间】:2017-10-09 09:54:11
【问题描述】:

我在 Flutter 网站上阅读了 introduction to platform-specific plugins/channels 并浏览了一些简单的插件示例,例如 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?

【问题讨论】:

    标签: android ios unit-testing dart flutter


    【解决方案1】:

    您可以使用 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);
    

    【讨论】:

    【解决方案2】:

    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);
    }
    

    【讨论】:

    【解决方案3】:

    当您创建插件时,系统会自动为您提供默认测试:

    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 允许您绕过实际插件所做的一切并返回您自己的值。
    • 您可以使用methodCall.method 来区分方法,它是被调用方法名称的字符串。
    • 对于插件创建者,这是一种验证公共 API 名称的方法,但它不会测试 API 的功能。您需要为此使用集成测试。

    【讨论】:

      猜你喜欢
      • 2019-02-01
      • 2019-12-28
      • 2022-01-16
      • 1970-01-01
      • 2023-02-08
      • 1970-01-01
      • 1970-01-01
      • 2019-10-05
      • 1970-01-01
      相关资源
      最近更新 更多