🤖
RiverpodのNotifierで作ったViewModelをmockitoでAPIをモック化してテストする
Flutter
でRiverpod
のNotifier
で作ったViewModel
において、APIリクエストを含むViewModel
のメソッドを、mockito
でAPIをモック化することによりテストしてみたので、最小のコード例を紹介します。
参考文献
アプリ側コード
-
dart run build_runner build
は適宜実行
API
- dio
- retrofit
GithubSearchApi githubSearchApi(GithubSearchApiRef ref) {
return GithubSearchApi(ref.watch(githubDioProvider));
}
()
abstract class GithubSearchApi {
factory GithubSearchApi(Dio dio) = _GithubSearchApi;
('/search/repositories')
Future<GithubSearchRepositoriesResponse> searchRepositories(
('q') String searchKeyword,
);
}
viewModel
class HomeTabViewModel extends _$HomeTabViewModel {
HomeTabState build() => const HomeTabState(repositories: []);
GithubSearchApi get _githubSearchApi => ref.watch(githubSearchApiProvider);
Future<void> search(String searchWord) async {
final response = await _githubSearchApi.searchRepositories(searchWord);
state = state.copyWith(repositories: response.items);
}
}
class HomeTabState with _$HomeTabState {
const factory HomeTabState({
([]) List<GithubRepository> repositories,
}) = _HomeTabState;
}
テストコード
([MockSpec<GithubSearchApi>()])
void main() {
ProviderContainer makeProviderContainer(MockGithubSearchApi githubSearchApi) {
final container = ProviderContainer(
overrides: [githubSearchApiProvider.overrideWithValue(githubSearchApi)],
);
addTearDown(container.dispose);
return container;
}
group('home_tab_view_model', () {
test('search', () async {
final githubSearchApi = MockGithubSearchApi();
final searchResultRepositories = [
GithubRepository(
name: 'repository-1',
fullName: 'repository-1',
language: 'lang',
stargazersCount: 1,
watchersCount: 2,
forksCount: 3,
issuesCount: 4)
];
when(githubSearchApi.searchRepositories('repo')).thenAnswer((_) {
return Future.value(GithubSearchRepositoriesResponse(
totalCount: 1,
imcompleteResults: false,
items: searchResultRepositories,
));
});
final container = makeProviderContainer(githubSearchApi);
container.listen(homeTabViewModelProvider, (previous, next) {
debugPrint('');
}, fireImmediately: true);
// メソッドを実行する前のStateをテスト
const expectedStateBeforeSearch = HomeTabState(repositories: []);
final actualStateBeforeSearch = container.read(homeTabViewModelProvider);
expect(actualStateBeforeSearch, expectedStateBeforeSearch);
final homeTabViewModel =
container.read(homeTabViewModelProvider.notifier);
// viewModelのメソッドを実行
await homeTabViewModel.search('repo');
// メソッドを実行した後のStateを確認
final expectedStateAfterSearch =
HomeTabState(repositories: searchResultRepositories);
final actualStateAfterSearch = container.read(homeTabViewModelProvider);
expect(actualStateAfterSearch, expectedStateAfterSearch);
});
});
}
Discussion