James
James

Reputation: 753

Testing Flutter code that uses a plugin and platform channel

I have a flutter plugin which uses the platform channel to do some native work.

How do I properly write tests for my application that requires this plugin?

Unit tests only are good for pure dart functions. I don't believe Widget testing will be able to test things that use the platform channel to native. So that leaves integration testing.

From what I understand is that integration testing will start your main application and you can control it around your app and test things.

For my case, I want to test just the code that uses the plugin (that uses the platform channel for native stuff).

Also what is important is the values that come back from the platform channel, so it is important to call the native side using a real platform channel and not a mock one.

Is that possible? Can I tell the integration tester to open a dummy version of my application, kind of like an integrated widget tester?

Upvotes: 17

Views: 6701

Answers (3)

Amr Ahmed
Amr Ahmed

Reputation: 203

One additional thing to @呂學洲 answer.. is if you're writing a unit test and don't have reference to widget tester instance you can access the binder like below

TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger

Also make sure to wait for binder to initialize in the begging of the test like below

TestWidgetsFlutterBinding.ensureInitialized();

And to grab method call you can do:

final binaryBinding = TestDefaultBinaryMessengerBinding.instance;
pluginChannel = MethodChannel('channelName');

binaryBinding!.defaultBinaryMessenger.setMockMethodCallHandler(pluginChannel,
  (MethodCall methodCall) async {
    switch (methodCall.method) {
      case 'call 1':
        return  mockValue;
      default:
        return null;
    }
  },
);

Hope that helps.

Upvotes: 2

呂學洲
呂學洲

Reputation: 1413

Flutter team do mentions that they want to do more things in widgets test instead of driver(integration) test.

We're moving away from flutter_driver in favour of extending flutter_test to work on devices.

From Flutter 2.8 breaking changes doc, you should use binding in tester instead of setMockMethodCallHandler from channel.

// old code
myMethodChannel.setMockMethodCallHandler(...);
myMethodChannel.checkMockMethodCallHandler(...);
// new code
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(myMethodChannel, ...);
tester.binding.defaultBinaryMessenger.checkMockMessageHandler(myMethodChannel, ...);

Take ImagePicker as example:

widgetTest('', (tester) async {
  const channel = MethodChannel('plugins.flutter.io/image_picker');
  tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
    channel,
    (MethodCall methodCall) => Future.value('some-image'),
  );
});

Upvotes: 2

mmccabe
mmccabe

Reputation: 2309

It seems the short answer to your question is no. Flutter driver (integration testing) can only interact with the UI, AFAIK. It cannot intercept calls to plugins. It is used to test the entire app from the UI.

However it is possible to intercept calls to plugins in unit and widget tests. This allows monitoring the calls to the plugin and mocking the response. That way you can test your plugin's dart code and/or a widget that uses the plugin. Testing native code would involve writing native tests.

The following is an example of intercepting calls to a plugin for testing:

MethodChannel('audio_recorder')
    .setMockMethodCallHandler((MethodCall methodCall) async {
  log.add(methodCall);
  switch (methodCall.method) {
    case 'start':
      isRecording = true;
      return null;
    case 'stop':
      isRecording = false;
      return {
        'duration': duration,
        'path': path,
        'audioOutputFormat': extension,
      };
    case 'isRecording':
      return isRecording;
    case 'hasPermissions':
      return true;
    default:
      return null;
  }
});

For a complete example see here

Upvotes: 17

Related Questions