🛟

【Flutter】AppLifecycleStateを変化させた場合のテストをする方法

2024/12/25に公開

はじめに

flutter_hooksにあるuseOnAppLifecycleStateChangeを使った場合のテストを実装したので備忘録も兼ねて残したいと思います。
ライフサイクル系で行いたいものは、バックグラウンドから復帰した場合にxxxの処理をする、といったものだと思います。
そのテスト内容をご紹介します。

記事の対象者

  • useOnAppLifecycleStateChangeを使ったテストで悩んでいる方

記事を執筆時点での筆者の環境

[✓] Flutter (Channel stable, 3.24.1, on macOS 15.0.1 24A348 darwin-arm64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 16.0)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.3)
[✓] VS Code (version 1.94.0)

サンプルプロジェクト

アプリの中央に配置されたテキストがあります。
アプリの初回起動時、バックグラウンドに行って復帰した場合1回目、バックグラウンドに行って復帰した場合2回目でそれぞれテキストの文字列が変化するようになっています。

ソースコード

一部抜粋

class HomeScreen extends HookWidget {
  const HomeScreen({
    super.key,
  });

  static const String firstMessage = 'Hello World!';
  static const String secondMessage = 'Welcome back!';
  static const String thirdMessage = 'Welcome back again!';

  
  Widget build(BuildContext context) {
    final message = useState(firstMessage);
    final counter = useState(0);

    useOnAppLifecycleStateChange((previous, current) {
      if (current == AppLifecycleState.resumed && counter.value == 0) {
        message.value = secondMessage;
        counter.value++;
      } else if (current == AppLifecycleState.resumed && counter.value >= 1) {
        message.value = thirdMessage;
        counter.value++;
      }
    });

    return Scaffold(
    // 省略
全文
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends HookWidget {
  const HomeScreen({
    super.key,
  });

  static const String firstMessage = 'Hello World!';
  static const String secondMessage = 'Welcome back!';
  static const String thirdMessage = 'Welcome back again!';

  
  Widget build(BuildContext context) {
    final message = useState(firstMessage);
    final counter = useState(0);
    useOnAppLifecycleStateChange((previous, current) {
      if (current == AppLifecycleState.resumed && counter.value == 0) {
        message.value = secondMessage;
        counter.value++;
      } else if (current == AppLifecycleState.resumed && counter.value >= 1) {
        message.value = thirdMessage;
        counter.value++;
      }
    });

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              message.value,
              style: const TextStyle(fontSize: 24),
            ),
            const SizedBox(height: 20),
            Text(
              'Counter: ${counter.value}',
              style: const TextStyle(fontSize: 24),
            ),
          ],
        ),
      ),
    );
  }
}

テストコード

import 'dart:ui';

import 'package:_sample/main.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  group('home_screen test', () {
    testWidgets('home_screen test', (WidgetTester tester) async {
      // メインウィジェットを作成
      await tester.pumpWidget(const MainApp());

      // アプリが立ち上がった際の最初のメッセージとカウンターの値を確認
      expect(find.text(HomeScreen.firstMessage), findsOneWidget);
      expect(find.text('Counter: 0'), findsOneWidget);

      // アプリを一度バックグラウンドに移動
      tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.paused);
      // アプリをフォアグラウンドに戻す
      tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
      // アプリのビルドを実行
      await tester.pump();

      // アプリがフォアグラウンドに戻った際のメッセージとカウンターの値を確認:1回目
      expect(find.text(HomeScreen.secondMessage), findsOneWidget);
      expect(find.text('Counter: 1'), findsOneWidget);

      // アプリを一度バックグラウンドに移動
      tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.paused);
      // アプリをフォアグラウンドに戻す
      tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
      // アプリのビルドを実行
      await tester.pump();

      // アプリがフォアグラウンドに戻った際のメッセージとカウンターの値を確認:2回目
      expect(find.text(HomeScreen.thirdMessage), findsOneWidget);
      expect(find.text('Counter: 2'), findsOneWidget);
    });
  });
}

tester.binding.handleAppLifecycleStateChanged();で変化させたい状態を渡せば良いようです。
注意点として状態を変化させた後に必ずawait tester.pump()によってアプリのビルドを実行することです。

ちなみに今回の方法はflutter_hooksがパッケージとしてのテストコードを書いていたので、そちらを参考にさせていただきました。

https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/test/use_app_lifecycle_state_test.dart

終わりに

ご覧いただきありがとうございました。
この記事が皆様のお役に立てれば幸いです。

Discussion