2
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?

可茂IT塾Advent Calendar 2023

Day 14

[Flutter] Widgetテストにて、FirebaseAuthのテストをする

Last updated at Posted at 2023-12-19

この記事は、【 可茂IT塾 Advent Calendar 2023 】の14日目の記事です。

TDD(テスト駆動開発)を行う時に、FirebaseAuth の認証系のテストについて
色々と調べたので、基本的な部分を記事にしました。誰かのお役に立てれば、幸いです。

この記事では、Flutterのテストについては詳しく説明していないので、
知りたい方は下の記事などが参考になると思われます。

この記事では、Widgetテストにおける FirebaseAuth のテストについて書いてあります。
実機やエミュレーターを使用しないWidgetテストでは、FirebaseAuth のテストは、モックを使用して行います。

モック用のパッケージが用意されており、こちらを使用します。

ちなみに、状態管理は riverpod です。

ドキュメントにもテストの項目が用意されているので、こちらも参考になります。

はじめに

今回テストするのは、signOut のメソッドだとします。下記がコード例です。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'auth.g.dart';

@Riverpod(keepAlive: true)
FirebaseAuth firebaseAuth(FirebaseAuthRef ref) => FirebaseAuth.instance;

@riverpod
UserAuth userAuth(UserAuthRef ref) {
  final instance = ref.watch(firebaseAuthProvider);
  return UserAuth(auth: instance);
}

class UserAuth {
  UserAuth({
    required FirebaseAuth auth,
  }) : _auth = auth;

  final FirebaseAuth _auth;

  Future<void> signOut() async {
    await _auth.signOut();
  }
}

FirebaseAuth.instance を Provider に持たせておきます。

下記が、テストするWidgetです。

ElevatedButton(
      onPressed: () async {
        final userAuth = ref.watch(userAuthProvider);
        await userAuth.signOut();
      },
      child: const Text('Sign Out!'),
    ),

次にテストのコードですが、FirebaseAuth のテストは、firebase_auth_mocks の Github に
コード例が書かれており、そちらがとても参考になります。

テストのコードです。

import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:x_test/auth/auth.dart';
import 'package:x_test/main.dart';

void main() {
  late MockFirebaseAuth mockFirebaseAuth;

  setUp(
    () {
      final tUser = MockUser(
        isAnonymous: false,
        email: 'bob@thebuilder.com',
        displayName: 'Bob Builder',
      );
      mockFirebaseAuth = MockFirebaseAuth(signedIn: true, mockUser: tUser);
    },
  );

  testWidgets('Sign Out Test', (WidgetTester tester) async {
    await tester.pumpWidget(
      ProviderScope(
        overrides: [
          firebaseAuthProvider.overrideWithValue(mockFirebaseAuth),
        ],
        child: const MaterialApp(
          home: MyApp(),
        ),
      ),
    );

    // ログインしていることの確認
    expect(mockFirebaseAuth.currentUser, isNotNull);

    final signOutButtonFinder =
        find.widgetWithText(ElevatedButton, 'Sign Out!');

    // 描画を確認
    expect(signOutButtonFinder, findsOneWidget);

    // Buttonをタップ
    await tester.tap(signOutButtonFinder);
    await tester.pumpAndSettle();

    // ログアウトしたことの確認
    expect(mockFirebaseAuth.currentUser, isNull);
  });
}

上記のテストコードのように、FirebaseAuth.instance をモックの MockFirebaseAuth に override することで、signOut のメソッドを、モックの signOut メソッドに切り替えることができます。

この override さえできてしまえば、あとはサンプルコードを参考にして、createUserWithEmailAndPassword や signInAnonymously など、ほかのFirebaseAuth のテストに応用することができると思われます。

終わりに

riverpod のドキュメントにも書いてありますが、
テスト時にモックやスタブを返すクラスに override することを前提にアプリを設計しておくことが、効率よくテストする上でとても重要だと感じました。

もし、間違っているところやよりよい方法をご存知の方がいれば、ご指摘いただけると助かります。

2
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
2
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?