Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import 'package:flutter/services.dart' show PlatformException;

void main() {
final MockUrlLauncher mock = MockUrlLauncher();
when(mock.isMock).thenReturn(true);
UrlLauncherPlatform.instance = mock;

test('closeWebView default behavior', () async {
Expand Down Expand Up @@ -208,4 +207,4 @@ void main() {
});
}

class MockUrlLauncher extends Mock implements UrlLauncherPlatform {}
class MockUrlLauncher extends Mock with MockPlatformInterface implements UrlLauncherPlatform {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.0.4

* Remove `@visibleForTesting` API `isMock`.
* Introduces `MockPlatformInterface` and `PlatformInterface` classes.

## 1.0.3

* Minor DartDoc changes and add a lint for missing DartDocs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,85 @@ import 'package:meta/meta.dart' show required, visibleForTesting;

import 'method_channel_url_launcher.dart';

/// Base class for platform interfaces.
///
/// Provides a static helper method for ensuring that platform interfaces are
/// implemented using `extends` instead of `implements`.
// TODO(amirh): Extract common platform interface logic.
// https://github.com/flutter/flutter/issues/43368
abstract class PlatformInterface {
/// Pass a private, class-specific `const Object()` as the `token`.
PlatformInterface({@required Object token}) : _instanceToken = token;

final Object _instanceToken;

/// Ensures that the platform instance has a token that matches the
/// provided token and throws [AssertionError] if not.
///
/// This is used to ensure that implementers are using `extends` rather than
/// `implements`.
///
/// Subclasses of [MockPlatformInterface] are assumed to be valid in debug
/// builds.
///
/// This is implemented as a static method so that it cannot be overridden
/// with `noSuchMethod`.
static void verifyToken(PlatformInterface instance, Object token) {
if (identical(instance._instanceToken, MockPlatformInterface._token)) {
bool assertionsEnabled = false;
assert(() {
assertionsEnabled = true;
return true;
}());
if (!assertionsEnabled) {
throw AssertionError(
'`MockPlatformInterface` is not intended for use in release builds.');
}
}
if (!identical(token, instance._instanceToken)) {
throw AssertionError(
'Platform interfaces must not be implemented with `implements`');
}
}
}

/// A [PlatformInterface] mixin that can be combined with mockito's `Mock`.
///
/// It passes the [PlatformInterface.verifyToken] check even though it isn't
/// using `extends`.
///
/// This class is intended for use in tests only.
@visibleForTesting
abstract class MockPlatformInterface implements PlatformInterface {
static const Object _token = Object();

@override
Object get _instanceToken => _token;
}

/// The interface that implementations of url_launcher must implement.
///
/// Platform implementations should extend this class rather than implement it as `url_launcher`
/// does not consider newly added methods to be breaking changes. Extending this class
/// (using `extends`) ensures that the subclass will get the default implementation, while
/// platform implementations that `implements` this interface will be broken by newly added
/// [UrlLauncherPlatform] methods.
abstract class UrlLauncherPlatform {
/// Only mock implementations should set this to true.
///
/// Mockito mocks are implementing this class with `implements` which is forbidden for anything
/// other than mocks (see class docs). This property provides a backdoor for mockito mocks to
/// skip the verification that the class isn't implemented with `implements`.
@visibleForTesting
bool get isMock => false;
abstract class UrlLauncherPlatform extends PlatformInterface {
UrlLauncherPlatform() : super(token: _token);

static UrlLauncherPlatform _instance = MethodChannelUrlLauncher();

static const Object _token = Object();

/// The default instance of [UrlLauncherPlatform] to use.
///
/// Defaults to [MethodChannelUrlLauncher].
static UrlLauncherPlatform get instance => _instance;

/// Platform-specific plugins should set this with their own platform-specific
/// class that extends [UrlLauncherPlatform] when they register themselves.
// TODO(amirh): Extract common platform interface logic.
// https://github.com/flutter/flutter/issues/43368
static set instance(UrlLauncherPlatform instance) {
if (!instance.isMock) {
try {
instance._verifyProvidesDefaultImplementations();
} on NoSuchMethodError catch (_) {
throw AssertionError(
'Platform interfaces must not be implemented with `implements`');
}
}
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}

Expand Down Expand Up @@ -72,12 +115,4 @@ abstract class UrlLauncherPlatform {
Future<void> closeWebView() {
throw UnimplementedError('closeWebView() has not been implemented.');
}

// This method makes sure that UrlLauncher isn't implemented with `implements`.
//
// See class doc for more details on why implementing this class is forbidden.
//
// This private method is called by the instance setter, which fails if the class is
// implemented with `implements`.
void _verifyProvidesDefaultImplementations() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ author: Flutter Team <flutter-dev@googlegroups.com>
homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
version: 1.0.3
version: 1.0.4

dependencies:
flutter:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ void main() {
});

test('Can be mocked with `implements`', () {
final ImplementsUrlLauncherPlatform mock =
ImplementsUrlLauncherPlatform();
when(mock.isMock).thenReturn(true);
final UrlLauncherPlatform mock =
ImplementsUrlLauncherPlatformUsingMockPlatformInterface();
UrlLauncherPlatform.instance = mock;
});

Expand Down Expand Up @@ -283,4 +282,8 @@ void main() {
class ImplementsUrlLauncherPlatform extends Mock
implements UrlLauncherPlatform {}

class ImplementsUrlLauncherPlatformUsingMockPlatformInterface extends Mock
with MockPlatformInterface
implements UrlLauncherPlatform {}

class ExtendsUrlLauncherPlatform extends UrlLauncherPlatform {}