LoginSignup
6
0

More than 1 year has passed since last update.

Flutterでfirebaseのauthとfirestoreを取り扱うriverpodを使ったunit testを構築する

Last updated at Posted at 2022-05-04

こんにちは。virapture株式会社もぐめっとです。

mogmet.jpg
最近スキー場で暁のメンバーに遭遇しました。思わず忍術を唱えてしまいました。

本日はテスト駆動開発大好きなもぐめっとがflutter X firestore X riverpodを使ったunit testの方法を模索してたどり着いた方法を紹介いたします。

結論から言うと、emulatorは使えないので代用ライブラリを使って構築します!!

今回のサンプルは下記に作ってありますのでよかったらご参考ください。

前知識

調べた過程を紹介します。
(構築方法だけ知りたい場合は次の章へスキップしてください)

いつもfirestoreを使ったunit testを書くときはemulatorを用いていました。

(unityやandroidは下記のように構築してました。)

いつものノリでemulatorを使っていい感じにできるっしょと思って調べたら・・・

I'm saying that using Firebase Emulators for your integration tests will work. But the example you're using is not an integration test, it is a unit test. A unit test does not involve any native API calls (to android or iOS for example). You have to mock the calls to the native platform (i.e Firebase.initializeApp()).

integration testでないと動かなそうというのと、integration testは遅いとのことでemulatorを使ったunit testは微妙そうでした。
(issueではmockすれば動くみたいな感じで書かれてましたが残念ながら動かせませんでした)

構築方法

ということでどうしたらいいかを調べたところfirebase_auth_mocksfake_cloud_firestoreというライブラリを使うことで代用できそうでした。

fake_cloud_firestoreの説明文も見る感じ大体対応してて良さそうです。

ということで今回はUserを取り扱うUserRepositoryに対してテストを書いてみることにしました。

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_firestore_unittest/providers.dart';
import 'package:flutter_firestore_unittest/user.dart' as app; // firebase authのUserと被るので別名定義
import 'package:flutter_riverpod/flutter_riverpod.dart';

// riverpodで使うためabstractにしてます。
abstract class AUserRepository {
  // ユーザ取得
  Future<app.User?> fetchUser(String authId);

  // 匿名ユーザの作成
  Future<app.User> createUser();
}

class UserRepository implements AUserRepository {
  UserRepository(Reader reader)
      : _firestore = reader(firebaseFirestoreProvider),
        _auth = reader(firebaseAuthProvider);
  final FirebaseFirestore _firestore;
  final FirebaseAuth _auth;

  CollectionReference get _collection => _firestore.collection('users');

  DocumentReference _getUserReference(String authId) {
    return _collection.doc(authId);
  }

  @override
  Future<app.User?> fetchUser(String authId) async {
    final snapshot = await _getUserReference(authId).get();
    return app.User.fromSnapshot(snapshot);
  }

  @override
  Future<app.User> createUser() async {
    final authUser = await _auth.signInAnonymously();
    final authId = authUser.user?.uid ?? '';
    final user = app.User(authId: authId, username: 'ワンナイト人狼だいすキッズ');
    await _getUserReference(authId).set(user.toJson());
    return user;
  }
}

テストはriverpodと組み合わせるテストにしてますが、こんな感じで書けるようになりました。

import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
import 'package:flutter_firestore_unittest/providers.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

@Timeout(Duration(seconds: 2))
void main() {
  late ProviderContainer container;
  const uid = 'werewolf';
  setUp(() async {
    TestWidgetsFlutterBinding.ensureInitialized();
    final mockUser = MockUser(isAnonymous: true, uid: uid); // FirebaseAuthで返却予定のユーザ
    // authとfirestoreをそれぞれmock化しちゃいます。
    container = ProviderContainer(
      overrides: [
        firebaseAuthProvider.overrideWithProvider(
          Provider((ref) => MockFirebaseAuth(mockUser: mockUser)),
        ),
        firebaseFirestoreProvider.overrideWithProvider(
          Provider((ref) => FakeFirebaseFirestore()),
        ),
      ],
    );
  });
  test('ユーザが作成できるか', () async {
    await container.read(userRepositoryProvider).createUser();
    final user = await container.read(userRepositoryProvider).fetchUser(uid);
    expect(user!.authId, uid);
    expect(user.username, 'ワンナイト人狼だいすキッズ');
  });
}

まるでemulatorを使ってるかのように振る舞ってくれました!!これはありがたい!!

一応サンプルを再掲しておきます。

注意事項

firebase_auth_mocksに関しての注意事項になります。

SNSログインはGoogleだけ

Google signinは別途google_sign_in_mocksを使って実施できるみたいですが、twitter/apple/facebookといったモックログインには対応してないみたいなので、どれのsignin方法を選んでもロジック的になるべく同じロジックで処理が通るようにコードを書く必要がありそうです。
ロジックさえ合わせられればgoogle signinのunit testだけでなんとかなりそうと思っています。

Emailログインでの登録はmockUserが上書きされる

createUserWithEmailAndPasswordのメソッドでログインしようとするとuidがmock_uidとして上書きされちゃいますので要注意です。
(多分バグ)

該当コード↓

まとめ

fake_cloud_firestorefirebase_auth_mocksを使うことで、flutterでも快適なunit test生活を送れます!!!!!

最後に、ワンナイト人狼オンラインというゲームを作ってます!よかったら遊んでね!

他にもCameconOffchaといったサービスも作ってるのでよかったら使ってね!

また、チームビルディングや技術顧問、Firebaseの設計やアドバイスといったお話も受け付けてますので御用の方は弊社までお問い合わせください。

6
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
6
0