[Flutter] Riverpod(hooks_riverpod)でWidget Test時にProviderを上書きする書き方のサンプル

2021/02/18に公開

☆初めての記事投稿になります。間違い等ありましたらご指摘のほどよろしくお願い致します☆

環境

  • Dart: 2.12.0
  • Flutter: 1.26.0-17.5.pre
  • hooks_riverpod: 0.12.4
  • freezed: 0.12.7

この記事の目的

公式DocsのTestingページには1つのdartファイルでWidget定義とTestを行っている例が書いてありますが、一般的には別ファイルで書くことが多いはずです。
Riverpodではテスト時にProviderの実装を書き換えることができます。自分でWidget Testを書いてる際に勘違いをしてTestが通らず考え込んでしまったので、備忘録として書いておきます。

自分のしていた勘違い

Widget Test時にはTestを書いたのと同じdartファイル内で定義したProviderが呼ばれると思っていましたが、呼ばれるのはあくまでもuseProvider等で呼び出しているProviderです。[1]

Widget定義とTestを別に書くサンプル

ここからは、サンプルコードを書いておきます。参考になれば幸いです。

state/auth/user/auth_state.dart

abstract class AuthState with _$AuthState {
  const factory AuthState({
    (false) bool isAuthenticated
  }) = _AuthState;
}

class AuthStateNotifier extends StateNotifier<AuthState> {
  AuthStateNotifier() : super(const AuthState());

  AuthStateNotifier.withDefaultValue({ bool isAuthenticated})
      : super(AuthState(isAuthenticated: isAuthenticated));

  set isAuthenticated(bool authenticationState) {
    state = state.copyWith(isAuthenticated: authenticationState);
  }

  bool get isAuthenticated {
    return state.isAuthenticated;
  }
}
lib/middleware/auth_middleware.dart
final authStateNotifierProvider =
    StateNotifierProvider((_) => AuthStateNotifier());

class AuthMiddleWare extends HookWidget {
  const AuthMiddleWare(this.amplifyApi);

  final AmplifyApiInterface amplifyApi;

  
  Widget build(BuildContext context) {
    final authStateNotifier = useProvider(authStateNotifierProvider);

    if (authStateNotifier.isAuthenticated) {
      return HomeView();
    } else {
      return StartupView();
    }
  }
}

Test codeはこんな感じ。

test/widget/middleware/auth_middleware_test.dart
void main() {
  group('AuthMiddleware', () {
    testWidgets('should go to HomeView Page when authenticated',
        (WidgetTester tester) async {
      await tester.pumpWidget(
        ProviderScope(
	  // ここでProviderの実装を上書き。authStateNotifierProviderを呼ぶことに注意。
          overrides: [
            authStateNotifierProvider.overrideWithValue(
              AuthStateNotifier.withDefaultValue(isAuthenticated: true),
            ),
          ],
          child: MaterialApp(
            home: AuthMiddleWare(),
          ),
        ),
      );

      expect(find.byType(HomeView), findsOneWidget);
    });
  });
}
脚注
  1. よく考えれば当たり前のことでした... ↩︎

Discussion