Flutter模拟通信插件channel_mock的使用

Flutter模拟通信插件channel_mock的使用

Channel Mock

Codemagic构建状态 Pub版本

此包用于在Flutter中帮助模拟插件通信通道(MethodChannel)。

该插件也可以在 pub 上找到。

开始使用

请参考 两个示例 Dart 文件 来了解如何使用此包。

示例代码

示例1:扩展ChannelMock

// Copyright (c) 2018, Brian Armstrong. 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_test/flutter_test.dart';
import 'package:flutter/services.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'package:channel_mock/channel_mock.dart';

/* -------------------------------- */
/* 示例为扩展ChannelMock */
/* -------------------------------- */

// 扩展ChannelMock 在你有一个固定集合需要模拟时非常有用。
class FirebaseAuthMock extends ChannelMock {
  FirebaseAuthMock() : super(MethodChannel('plugins.flutter.io/firebase_auth'));

  final mockUserData = {
    'uid': 'mock-uid',
    'providerData': [
      {
        'providerId': 'mock-provider',
        'uid': 'mock-provider-id',
      },
    ],
  };

  @override
  void reset() {
    super.reset();

    // 当调用'methodName'时返回特定数据
    when('currentUser').thenReturn(mockUserData);
    when('getIdToken').thenReturn(mockUserData['uid']);

    // 模拟平台响应
    when('startListeningAuthState').thenRespond(
      'onAuthStateChanged',
      (handle, _) => <String, dynamic>{
        'id': handle,
        'user': mockUserData,
      },
    );

    // 如果没有特定模拟,则返回默认值
    otherwise().thenReturn(mockUserData);
  }
}

// 然后可以在测试中简单地使用它
void mainExt() {
  final mock = new FirebaseAuthMock();

  setUp(() {
    mock.reset();
  });

  test('它已经模拟了onAuthStateChanged监听器', () async {
    // 注意:无需对模拟进行额外设置

    // 使用Completer以便等待onAuthStateChanged的结果
    final doneListening = new Completer<User?>();

    // 触发我们的代码并完成用户查找
    FirebaseAuth.instance.authStateChanges().listen((user) {
      doneListening.complete(user);
    });

    // *magic*
    final user = await doneListening.future;
    expect(user, isNotNull);
    expect(user!.uid, equals('mock-uid'));
  });
}

示例2:实现ChannelMock

// 这个库是为了在单元测试中模拟flutter方法通道
// 此示例文件是FirebaseAuth的模拟
void mainImpl() {
  const channel = const MethodChannel(
    'plugins.flutter.io/firebase_auth',
  );

  late ChannelMock mock;

  final mockUserData = {
    'uid': 'mock-uid',
    'providerData': [
      {
        'providerId': 'mock-provider',
        'uid': 'mock-provider-id',
      },
    ],
  };

  setUp(() {
    // 只需这一步,然后我们就可以使用这些方法了
    mock = new ChannelMock(channel);
  });

  test('简单的返回值', () async {
    // 每当调用'getIdToken'方法时,它将返回我们的模拟令牌
    mock.when('currentUser').thenReturn(mockUserData);

    final user = FirebaseAuth.instance.currentUser;
    expect(user, isNotNull);
    expect(user!.uid, equals('mock-uid'));
  });

  test('模拟平台响应后的调用', () async {
    // 对于FirebaseAuth,需要一个平台消息才能发生身份验证状态更改
    // 因此,当我们开始监听时,必须模拟此平台调用
    mock.when('startListeningAuthState').thenRespond(
        'authStateChanges',
        (handle, _) => <String, dynamic>{
              'id': handle,
              'user': mockUserData,
            });

    // 使用Completer以便等待onAuthStateChanged的结果
    final doneListening = new Completer<User>();

    // 触发我们的代码并完成用户查找
    FirebaseAuth.instance.authStateChanges().listen((user) {
      doneListening.complete(user);
    });

    // *magic*
    final user = await doneListening.future;
    expect(user.uid, equals('mock-uid'));
  });

  test('模拟Google登录', () async {
    mock.when('signInWithCredential').thenCall((handle, args) {
      // `handle` 是一个内部自增数字,表示已进行了多少次调用
      // 它通常被插件用于保持数据沿着正确的路径传递
      // `args` 是`channel.invokeMethod`传递的参数
      // 在这种情况下,它是Map<String, String>,但具体实现可能会有所不同
      Map newUser = Map.from(mockUserData);
      final cred = args as GoogleAuthCredential;
      newUser['email'] = '${cred.idToken}@${cred.accessToken}';

      // 我们从这个函数返回的内容将作为thenReturn返回
      return newUser;
    });

    final result = await FirebaseAuth.instance.signInWithCredential(
      GoogleAuthProvider.credential(
        idToken: 'mock-id-token',
        accessToken: 'mock-access-token',
      ),
    );
    expect(result.user?.uid, equals('mock-uid'));
    expect(result.user?.email, equals('mock-id-token@mock-access-token'));
  });

  test('我们只是想大多数事情都返回用户', () async {
    // ChannelMock.otherwise 是如果没有模拟MethodCall的方法,则返回的默认值
    mock.otherwise().thenReturn(mockUserData);

    // 因为我们没有专门模拟此方法,所以它只返回模拟用户数据
    final user = await FirebaseAuth.instance.createUserWithEmailAndPassword(
      email: 'mock-email',
      password: 'mock-password',
    );
    expect(user.user?.uid, equals('mock-uid'));
  });
}

更多关于Flutter模拟通信插件channel_mock的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter模拟通信插件channel_mock的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于在Flutter中使用channel_mock来模拟通信插件,这里是一个简要的示例。channel_mock库主要用于在单元测试中模拟Flutter平台通道(MethodChannel、BasicMessageChannel等),以避免依赖实际的原生代码。

首先,确保你已经在pubspec.yaml文件中添加了channel_mock依赖:

dependencies:
  flutter:
    sdk: flutter
  channel_mock: ^0.0.3  # 请检查最新版本号

然后运行flutter pub get来获取依赖。

以下是一个示例,展示了如何使用channel_mock来模拟一个MethodChannel

1. 创建一个Flutter插件接口

首先,我们定义一个Flutter插件接口,它将通过MethodChannel与原生代码通信。在实际应用中,这部分代码可能由插件自动生成。

import 'package:flutter/services.dart';

class MyPlugin {
  static const MethodChannel _channel = MethodChannel('com.example.myplugin');

  static Future<String?> get platformVersion async {
    final String? version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

2. 使用channel_mock进行单元测试

接下来,我们编写一个单元测试,使用channel_mock来模拟MethodChannel的行为。

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:channel_mock/channel_mock.dart';
import 'package:my_app/my_plugin.dart';  // 假设你的插件接口文件名为my_plugin.dart

void main() {
  late MethodChannelMock mockChannel;

  setUp(() {
    mockChannel = MethodChannelMock('com.example.myplugin');
    MethodChannel('com.example.myplugin').setMockMethodCallHandler(mockChannel.handler);
  });

  tearDown(() {
    MethodChannel('com.example.myplugin').setMockMethodCallHandler(null);
  });

  test('get platform version', () async {
    // 模拟平台返回的版本号
    when(mockChannel.invokeMethod('getPlatformVersion')).thenAnswer((_) async => '42');

    // 调用插件接口
    final String? version = await MyPlugin.platformVersion;

    // 验证结果
    expect(version, '42');

    // 验证方法被调用
    verify(mockChannel.invokeMethod('getPlatformVersion')).called(1);
  });
}

解释

  1. 定义插件接口MyPlugin类使用MethodChannel与原生代码通信。
  2. 设置测试环境:在setUp方法中,我们创建了一个MethodChannelMock实例,并将其设置为MethodChannel的模拟处理器。
  3. 编写测试用例:在test方法中,我们使用when来模拟invokeMethod调用的返回值,然后调用插件接口方法,并使用expect来验证结果。
  4. 清理:在tearDown方法中,我们清除模拟处理器,以避免对其他测试的影响。

这个示例展示了如何使用channel_mock来模拟Flutter平台通道的行为,以便在不依赖实际原生代码的情况下进行单元测试。希望这对你有所帮助!

回到顶部