flutter_inappwebview icon indicating copy to clipboard operation
flutter_inappwebview copied to clipboard

Add testing example to docs

Open GonzaloAldana opened this issue 1 year ago • 11 comments

Environment

Flutter version: 3.7.12 Plugin version: 6.0.0

Description

Hello! After upgrading the package version from 5.8.0 to 6.0.0 my tests started failing, I've spent some time taking a look at the documentation and searching through Google and Stack Overflow but nothing seems to work for me

The logs mention this:

A platform implementation for flutter_inappwebview has not been set. Please ensure that an implementation of InAppWebViewPlatform has been set to WebViewPlatform.instance before use. For unit testing, WebViewPlatform.instance can be set with your own test implementation.

What you'd like to happen:

Could you please provide a minimal reproducible test scenario for this to work? Would be really appreciated!

GonzaloAldana avatar Feb 13 '24 16:02 GonzaloAldana

👋 @GonzaloAldana

NOTE: This comment is auto-generated.

Are you sure you have already searched for the same problem?

Some people open new issues but they didn't search for something similar or for the same issue. Please, search for it using the GitHub issue search box or on the official inappwebview.dev website, or, also, using Google, StackOverflow, etc. before posting a new one. You may already find an answer to your problem!

If this is really a new issue, then thank you for raising it. I will investigate it and get back to you as soon as possible. Please, make sure you have given me as much context as possible! Also, if you didn't already, post a code example that can replicate this issue.

In the meantime, you can already search for some possible solutions online! Because this plugin uses native WebView, you can search online for the same issue adding android WebView [MY ERROR HERE] or ios WKWebView [MY ERROR HERE] keywords.

Following these steps can save you, me, and other people a lot of time, thanks!

github-actions[bot] avatar Feb 13 '24 16:02 github-actions[bot]

Facing same issue any update related this ?

asiteandroid avatar Feb 22 '24 05:02 asiteandroid

This worked

class MockWebViewPlatform extends Mock
    with MockPlatformInterfaceMixin
    implements InAppWebViewPlatform {}

class MockPlatformCookieManager extends Mock
    with MockPlatformInterfaceMixin
    implements PlatformCookieManager {}

class MockWebViewWidget extends Mock
    with MockPlatformInterfaceMixin
    implements PlatformInAppWebViewWidget {}

class FakeCookieParams extends Fake
    implements PlatformCookieManagerCreationParams {}

class FakeWebUri extends Fake implements WebUri {}

class FakeWidgetParams extends Fake
    implements PlatformInAppWebViewWidgetCreationParams {}

class MockWebViewDependencies {
  static const MethodChannel channel = MethodChannel('fk_user_agent');

  Future<void> init() async {
    registerFallbackValue(FakeCookieParams());
    registerFallbackValue(FakeWebUri());
    registerFallbackValue(FakeWidgetParams());

    // Mock webview widget
    final mockWidget = MockWebViewWidget();
    when(() => mockWidget.build(any())).thenReturn(const SizedBox.shrink());

    // Mock cookie manager
    final mockCookieManager = MockPlatformCookieManager();
    when(() => mockCookieManager.deleteAllCookies())
        .thenAnswer((_) => Future.value(true));
    when(() => mockCookieManager.setCookie(
          url: any(named: 'url'),
          name: any(named: 'name'),
          value: any(named: 'value'),
          path: any(named: 'path'),
          domain: any(named: 'domain'),
          expiresDate: any(named: 'expiresDate'),
          maxAge: any(named: 'maxAge'),
          isSecure: any(named: 'isSecure'),
          isHttpOnly: any(named: 'isHttpOnly'),
          sameSite: any(named: 'sameSite'),
          // ignore: deprecated_member_use
          iosBelow11WebViewController:
              any(named: 'iosBelow11WebViewController'),
          webViewController: any(named: 'webViewController'),
        )).thenAnswer((_) => Future.value(true));

    // Mock webview platform
    final mockPlatform = MockWebViewPlatform();
    when(() => mockPlatform.createPlatformInAppWebViewWidget(any()))
        .thenReturn(mockWidget);
    when(() => mockPlatform.createPlatformCookieManager(any()))
        .thenReturn(mockCookieManager);

    // Use mock
    InAppWebViewPlatform.instance = mockPlatform;

   

    // Mock user agent in setUp or setUpAll
    channel.setMockMethodCallHandler((MethodCall methodCall) async {
      return {'webViewUserAgent': 'userAgent'};
    });
    FkUserAgent.init();
  }

  void tearDown() {
    channel.setMockMethodCallHandler(null);
  }

  /// This double pump is needed for triggering the build of the webview
  /// otherwise it will fail
  Future<void> doublePump(WidgetTester tester) async {
    await tester.pump();
    await tester.pump();
  }
}

And add these to the tests

void main() {
  final mockWebViewDependencies = MockWebViewDependencies();

  setUp(() {
    mockWebViewDependencies.init();
  });

  tearDown(() {
    mockWebViewDependencies.tearDown();
  });
.
.
.

GonzaloAldana avatar Feb 22 '24 14:02 GonzaloAldana

I do Just like @GonzaloAldana said,

but I have some error.

Bad state: A test tried to use any or captureAny on a parameter of type BuildContext, but registerFallbackValue was not previously called to register a fallback value for BuildContext.

So I try to add those code: class FakeBuildContext extends Fake implements BuildContext {}

registerFallbackValue(FakeBuildContext());

And change setup callback to async function like this:

setUp(() async {
    await mockWebViewDependencies.init();
});

Its worked!

Environment

Flutter version: 3.19.3 Plugin version: 6.0.0

L4rue avatar Mar 12 '24 08:03 L4rue

Great thread!

I am trying to test a situation where onLoadStart is called again when a redirect url is called, I am mocking a lot of stuff but I am not able to trigger onLoadStart in widget tests, any ideas?

vlad-buhaescu avatar Mar 25 '24 15:03 vlad-buhaescu

This worked

class MockWebViewPlatform extends Mock
    with MockPlatformInterfaceMixin
    implements InAppWebViewPlatform {}

class MockPlatformCookieManager extends Mock
    with MockPlatformInterfaceMixin
    implements PlatformCookieManager {}

class MockWebViewWidget extends Mock
    with MockPlatformInterfaceMixin
    implements PlatformInAppWebViewWidget {}

class FakeCookieParams extends Fake
    implements PlatformCookieManagerCreationParams {}

class FakeWebUri extends Fake implements WebUri {}

class FakeWidgetParams extends Fake
    implements PlatformInAppWebViewWidgetCreationParams {}

class MockWebViewDependencies {
  static const MethodChannel channel = MethodChannel('fk_user_agent');

  Future<void> init() async {
    registerFallbackValue(FakeCookieParams());
    registerFallbackValue(FakeWebUri());
    registerFallbackValue(FakeWidgetParams());

    // Mock webview widget
    final mockWidget = MockWebViewWidget();
    when(() => mockWidget.build(any())).thenReturn(const SizedBox.shrink());

    // Mock cookie manager
    final mockCookieManager = MockPlatformCookieManager();
    when(() => mockCookieManager.deleteAllCookies())
        .thenAnswer((_) => Future.value(true));
    when(() => mockCookieManager.setCookie(
          url: any(named: 'url'),
          name: any(named: 'name'),
          value: any(named: 'value'),
          path: any(named: 'path'),
          domain: any(named: 'domain'),
          expiresDate: any(named: 'expiresDate'),
          maxAge: any(named: 'maxAge'),
          isSecure: any(named: 'isSecure'),
          isHttpOnly: any(named: 'isHttpOnly'),
          sameSite: any(named: 'sameSite'),
          // ignore: deprecated_member_use
          iosBelow11WebViewController:
              any(named: 'iosBelow11WebViewController'),
          webViewController: any(named: 'webViewController'),
        )).thenAnswer((_) => Future.value(true));

    // Mock webview platform
    final mockPlatform = MockWebViewPlatform();
    when(() => mockPlatform.createPlatformInAppWebViewWidget(any()))
        .thenReturn(mockWidget);
    when(() => mockPlatform.createPlatformCookieManager(any()))
        .thenReturn(mockCookieManager);

    // Use mock
    InAppWebViewPlatform.instance = mockPlatform;

   

    // Mock user agent in setUp or setUpAll
    channel.setMockMethodCallHandler((MethodCall methodCall) async {
      return {'webViewUserAgent': 'userAgent'};
    });
    FkUserAgent.init();
  }

  void tearDown() {
    channel.setMockMethodCallHandler(null);
  }

  /// This double pump is needed for triggering the build of the webview
  /// otherwise it will fail
  Future<void> doublePump(WidgetTester tester) async {
    await tester.pump();
    await tester.pump();
  }
}

And add these to the tests

void main() {
  final mockWebViewDependencies = MockWebViewDependencies();

  setUp(() {
    mockWebViewDependencies.init();
  });

  tearDown(() {
    mockWebViewDependencies.tearDown();
  });
.
.
.

Can you please add full example

Shweta-Vitality avatar Apr 02 '24 06:04 Shweta-Vitality

I use mockito so the above example didn't work out of the box. but I was able to get it to work with this code below:


class MockWebViewPlatform extends InAppWebViewPlatform
    with MockPlatformInterfaceMixin {
  @override
  PlatformInAppWebViewWidget createPlatformInAppWebViewWidget(
    PlatformInAppWebViewWidgetCreationParams params,
  ) {
    return MockWebViewWidget.implementation(params);
  }

  @override
  PlatformCookieManager createPlatformCookieManager(
    PlatformCookieManagerCreationParams params,
  ) {
    return MockPlatformCookieManager();
  }
}

class MockPlatformCookieManager extends Fake implements PlatformCookieManager {
  @override
  Future<bool> deleteAllCookies() async {
    return true;
  }

  @override
  Future<bool> setCookie({
    required WebUri url,
    required String name,
    required String value,
    String path = '/',
    String? domain,
    int? expiresDate,
    int? maxAge,
    bool? isSecure,
    bool? isHttpOnly,
    HTTPCookieSameSitePolicy? sameSite,
    PlatformInAppWebViewController? iosBelow11WebViewController,
    PlatformInAppWebViewController? webViewController,
  }) async {
    return true;
  }
}

class MockWebViewWidget extends PlatformInAppWebViewWidget {
  MockWebViewWidget.implementation(super.params) : super.implementation();

  @override
  Widget build(BuildContext context) {
    return const SizedBox.shrink();
  }

  @override
  T controllerFromPlatform<T>(PlatformInAppWebViewController controller) {
    // TODO: implement controllerFromPlatform
    throw UnimplementedError();
  }

  @override
  void dispose() {
    // TODO: implement dispose
  }

  @override
  // TODO: implement params
  PlatformInAppWebViewWidgetCreationParams get params =>
      throw UnimplementedError();
}

class MockWebViewDependencies {
  Future<void> init() async {
    // Mock webview platform
    final InAppWebViewPlatform mockPlatform = MockWebViewPlatform();

    // Use mock
    InAppWebViewPlatform.instance = mockPlatform;
  }
}

mike-edify avatar Apr 16 '24 07:04 mike-edify

@mike-edify Thank you! Thanks to this I was also able to test InAppBrowser!

It would be nice if the documentation describes how to write these tests!

ShuheiSuzuki-07 avatar Apr 26 '24 04:04 ShuheiSuzuki-07

@ShuheiSuzuki-07 You're welcome. Glad it helped someone. :D

mike-edify avatar Apr 26 '24 15:04 mike-edify

Thanks @L4rue @GonzaloAldana You're lifesavers!!! I am new to widget testing, and the issue was like the final boss to me haha, I learned a lot from your solutions. 🙏

TinhHuynh avatar May 10 '24 16:05 TinhHuynh

we are getting this issue in Plugin version: 6.0.0 if I use 5.8.0 then there is no error

Can anyone give me an complete example for InAppWebView @mike-edify @ShuheiSuzuki-07

ram-wappnet avatar Sep 19 '24 12:09 ram-wappnet