canonical / dbus.dart

Native Dart client library to use DBus.
https://pub.dev/packages/dbus
Mozilla Public License 2.0
93 stars 33 forks source link

FR: making mocking simple #336

Open daadu opened 2 years ago

daadu commented 2 years ago

Something similar to how it is done in flutter with - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(_methodChannel, _methodHandler);

Say for example, consider following dependecny graph my_app -> connectivity_plus -> connectivity_plus_linux -> dbus - know as a app developer - I would want to mock and test certain behaviour based on the result of where it is connected. It is possible to do this with other platform (like Android, iOS, mac which uses Flutter Platform Channels internally), but not with Linux as of now.

PS: I recently published mock_plugin - to simplify mocking "plugin" library for the end-application developer to mock plugin behaviours - turns out most plugins supporting Linux, uses dbus.dart - I would love to support that too with mock_plugin if there is a way to mock DBus client/channel/messages.

daadu commented 2 years ago

Providing a mechanism at dbus.dart level would allow by-passing "actual" calls for all plugins that uses dbus internally.

robert-ancell commented 2 years ago

@jpnurmi - you have more experience with this, WDYT?

jpnurmi commented 2 years ago

I'm all for making it easier to test D-Bus-based Dart and Flutter apps and packages. :+1:

Funny, we just discussed the other day about having to "pollute" code for mocking. :smile: Mocking out dbus.dart can be challenging even with direct access to DBusClient - in some cases, one needs to find a way to inject DBusRemoteObject mocks too. It's a pity that dbus.dart would need to be "polluted" with a mock-handler but as @daadu mentioned, sometimes the dependency is indirect and deeper under the hood...

P.S. @daadu, for that specific case, one could simply replace the ConnectivityPlatform instance with a fake (easier than a mock because of the instance token).

void main() {
  testWidgets('test with wifi', (tester) async {
    ConnectivityPlatform.instance =
        FakeConnectivityPlatform(ConnectivityResult.wifi);

    await tester.pumpWidget(...);
    await tester.pumpAndSettle();
    expect(foo, somethingThatExpectsWifi);
  });
}

class FakeConnectivityPlatform extends ConnectivityPlatform {
  FakeConnectivityPlatform(this._result);

  final ConnectivityResult _result;

  @override
  Future<ConnectivityResult> checkConnectivity() => Future.value(_result);

  @override
  Stream<ConnectivityResult> get onConnectivityChanged => const Stream.empty();
}
daadu commented 2 years ago

Almost forgot about this solution (setting Platform.instance), works for me as of now. Now that I think of this should be the prefered way of mocking plugins that use this (platform_interface) pattern, which almost all plugin that supports Linux does.

mocking actual messages/channels (MethodChannel or DBusClient) should only be preferred if platform_interface is not used by the plugin.