How can I mock/stub out a Flutter platform channel

2019-06-28 00:20发布

问题:

I read the 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,
  );
}

In widgets tests or integration tests, how can I mock out or stub channels so I don't have to rely on the real device (running Android or iOS) say, actually launching a URL?

回答1:

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


回答2:

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.