0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Flutter】url_launcher 最新版推奨APIのモック化・呼び出し検証(verify)

Last updated at Posted at 2025-03-11

概要

url_launcher を mockito でモック化し、指定のURLが開かれたことをウィジェットテストで検証しようと思い方法を探したところ下記の記事を見つけましたが、

これで対象としている canLaunch(), launch() は記事作成時点の最新版1 では非推奨2 なので
推奨の canLaunchUrl(), launchUrl() (以下「推奨API」と記します)で同様に検証する方法がないか探ったところ突き止めることができたので以下に紹介します。

環境

$ flutter doctor
[√] Flutter (Channel stable, 3.24.4, on Microsoft Windows [Version 10.0.19045.5487], locale ja-JP)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.3.6)
[!] Android Studio (version 2021.3)
    X Unable to determine bundled Java version.
[√] Android Studio (version 2024.1)
[√] VS Code (version 1.97.0)
[√] VS Code, 64-bit edition (version 1.64.2)
[√] Connected device (3 available)
[√] Network resources
pubspec.yaml(抜粋)
  url_launcher: ^6.3.1

dev_dependencies:
  build_runner: ^2.4.13
  mockito: ^5.4.4
  test: ^1.25.7

(先に)結論

下記のコードで推奨APIが呼び出されたことの検証が可能でした。

class FakeUrlLauncherPlatform extends Fake
  with MockPlatformInterfaceMixin
  implements UrlLauncherPlatform {}  // ここは冒頭参考記事と同じ

const linkUrl = 'https://www.hogehoge.com/';

// モックインスタンスの生成と挙動設定  setUp() 等で実行しても勿論OK
var mockUrlLauncherPlatform = MockFakeUrlLauncherPlatform();
UrlLauncherPlatform.instance = mockUrlLauncherPlatform;
when(mockUrlLauncherPlatform.canLaunch(linkUrl))
  .thenAnswer((_) async => true);  // <= ポイント1(後述)
when(mockUrlLauncherPlatform.launchUrl(linkUrl, any))
  .thenAnswer((_) async => true);  // <= ポイント2(後述)

// url_launcher が実行されるはずのコードをこの間に記載

// url_launcher(モック)が実行されたことの確認
verify(mockUrlLauncherPlatform.canLaunch(linkUrl));  // <= ポイント1(後述)
verify(mockUrlLauncherPlatform.launchUrl(linkUrl, any));  // <= ポイント2(後述)

解説

url_launcher (とそのモック)の裏側

build_runner で url_launcher_screen_test.mocks.dart を生成すると MockFakeUrlLauncherPlatform クラスは下記メソッドを持っていることが分かります。3

メソッドのリスト(クリックで展開)
MockFakeUrlLauncherPlatform のメソッドリスト
canLaunch(String? url)
launch(String? url, {
  required bool? useSafariVC,
  required bool? useWebView,
  required bool? enableJavaScript,
  required bool? enableDomStorage,
  required bool? universalLinksOnly,
  required Map<String, String>? headers,
  String? webOnlyWindowName,
})
launchUrl(String? url, LaunchOptions? options)
closeWebView()
supportsMode(PreferredLaunchMode? mode)
supportsCloseForMode(PreferredLaunchMode? mode)

ただ、よく見てみると推奨APIとは引数 url の型が違ったり(UriString?)、canLaunch Url () に至っては存在すらしません。
そのため MockFakeUrlLauncherPlatform に対し推奨APIと同じ名前・引数で whenverify を設定してみてもコンパイルに失敗します。

そこで今度は推奨API自体を url_launcher のソースから探してみると
url_launcher_uri.dart に定義があり
内側では UrlLauncherPlatform.instancecanLaunch()4 launchUrl() を実行していることが見て取れます。
テストでは UrlLauncherPlatform.instance == MockFakeUrlLauncherPlatform なので、これがどう実行されているか読み解くことでモック化・呼出検証の方法が分かりました。

テスト実装時のポイント

以上を踏まえ、テストコード例中のポイントを補記すると

  1. canLaunchUrl() は内部では非推奨のAPIと変わらず UrlLauncherPlatform.instance.canLaunch() を実行しているので、推奨API canLaunchUrl() でプロダクトを実装していても
    テストでのモック呼出検証は冒頭の参考記事と全く同様に canLaunch() に対し whenverify を行えばOKでした。
  2. 内部で実際に叩かれる UrlLauncherPlatform.instance.launchUrl() の第2引数 options
    推奨APIの launchUrl() にはない上に nullable なのでうっかり省略したくなりますが、略すと whenverify ではコンパイルが通らないので何らかの指定が必要です。
    とは言え単純に url_launcher で指定のURLが開かれたことを検証したいだけなら特定の値を指定する必要がないので any としておけば問題ありません5

テストコード全文

url_launcher_screen_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';

import 'url_launcher_screen.dart';  // テスト対象画面(詳細コードは割愛)
import 'url_launcher_screen_test.mocks.dart';

class FakeUrlLauncherPlatform extends Fake
  with MockPlatformInterfaceMixin
  implements UrlLauncherPlatform {}

@GenerateNiceMocks([MockSpec<FakeUrlLauncherPlatform>()])  //★(下方に補足あり)
void main() {
  const linkUrl = 'https://www.hogehoge.com/';
  late MockFakeUrlLauncherPlatform mockUrlLauncherPlatform;

  setUp(() {
    // モックインスタンスの生成と挙動設定
    mockUrlLauncherPlatform = MockFakeUrlLauncherPlatform();
    UrlLauncherPlatform.instance = mockUrlLauncherPlatform;
    when(mockUrlLauncherPlatform.canLaunch(linkUrl))
      .thenAnswer((_) async => true);  // <= ポイント1
    when(mockUrlLauncherPlatform.launchUrl(linkUrl, any))
      .thenAnswer((_) async => true);  // <= ポイント2
  });

  testWidgets('Testing Open In Browser', (tester) async {
    // URLをurl_launcherで開くボタン/リンクを持つテスト対象画面のロード
    await tester.pumpWidget(const MaterialApp(home: UrlLauncherScreen(url: linkUrl)));

    // url_launcherを実行するアクションを実行(この場合アイコンボタンをタップ)
    await tester.tap(find.byIcon(Icons.open_in_browser));
    await tester.pump();

    // url_launcher が実行されたことの確認
    verify(mockUrlLauncherPlatform.canLaunch(linkUrl));  // <= ポイント1
    verify(mockUrlLauncherPlatform.launchUrl(linkUrl, any));  // <= ポイント2
  });
}

★:冒頭参考記事では @GenerateMocks annotation でモックを generate しているところ
現行 mockito の推奨 annotation である @GenerateNiceMocks を用いても問題なかったのでその旨も付記しておきます。

  1. v6.3.1

  2. かと言ってテストのためにプロダクトコードを非推奨メソッドに差替えるのはあまりに下策…

  3. UrlLauncherPlatform クラスのメソッドとほぼ(コンストラクタと getter/setter を除けば引数等のI/Fまで全く)同じで、これを元にモックが生成されていることが分かる

  4. canLaunch Url () ではないことに注意

  5. 細かく追ってないですが options はプラットフォーム等の条件によって変化するように見えるので寧ろ any にしておかないとテストが不安定になるかもしれません

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?