起きたこと
単体テスト実装中に
Mockclient type 'Null' is not a subtype of type 'Future<Response>'
エラーの壁に当たったが最初からやり直したら改善したため共有
悩んだ点
-
mockClient.get(any)
ormockClient.get(any())
そもそもanyが使えない -
mockClient.get(uri)
uriを同じにしてもエラーが発生 - 何をしてもmockitoの
when
でテストが上記エラーでコケる
改善方法
@GenerateMocks
アノテーションを付け!
$ flutter pub run build_runner build
を実行し!!
MockClientクラスを自動生成することで全て解決された!!!
以下手順を記載していきます。参考になれば幸いです
テスト対象
listRepository
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
class ListRepository extends ApiBaseModel {
ListRepository(this.client);
final Client client;
@override
Map<String, String> header() => {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${token}'
};
@override
String path() => '/api/v1/list';
@override
Future<Response> fetch() {
final uri = Uri.https(domain(), path());
return client
.get(uri, headers: header())
.timeout(const Duration(seconds: 10), onTimeout: () {
return http.Response('Error', 500);
});
}
}
ApiBaseModel
はただのインターフェースですが、載せておきます。
ListRepository
に直接書いても問題ないため、今回の内容には特に関係ないです。
ApiBaseModel
abstract class ApiBaseModel {
Map<String, String> header() => {'Content-Type': 'application/json'};
String domain() => 'hogehoge.co.jp';
String path();
Future<Response> fetch();
}
テスト作成手順
- 型作成
- コマンド実行
- テストコード記載
型作成
app/test/list_repository.dart
import 'package:http/http.dart' as http;
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
@GenerateMocks([http.Client])
void main() {}
- 上記3点をimportする
-
@GenerateMocks
アノテーションを付与 - mainはとりあえず空
コマンド実行
Terminal等でapp/に移動し
flutter pub run build_runner build
を実行
同じ階層(app/test/)にlist_repository_test.mocks.dart
が作成されることを確認!
テストコード記載
app/test/list_repository.dart
import 'package:flutter_test/flutter_test.dart'; //追加
import 'package:http/http.dart' as http;
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'app/model/list/list_repository.dart'; //追加(テスト対象)
import 'list_repository_test.mocks.dart'; //追加(自動生成されたもの)
@GenerateMocks([http.Client])
void main() {
final mockClient = MockClient();
late ListRepository listRepository;
setUp(() {
listRepository = ListRepository(mockClient);
});
group('api test', () {
test('fetchList', () async {
const body = '''{
"id": 0,
"hogehoge": "fugafuga",
}''';
final uri = Uri.https(listRepository.domain(), listRepository.path());
when(mockClient.get(uri, headers: listRepository.header()))
.thenAnswer((_) => Future.value(http.Response(body, 200)));
final list = await listRepository.fetch();
expect(list.body, body);
});
});
}
簡易解説
setUp(() {
listRepository = ListRepository(mockClient);
});
- repositoryにて使用するclientをmockに差し替える
final uri = Uri.https(listRepository.domain(), listRepository.path());
when(mockClient.get(uri, headers: listRepository.header()))
.thenAnswer((_) => Future.value(http.Response(body, 200)));
- テスト対象のAPI Callと同等なuriを生成し、whenで予約
final list = await listRepository.fetch();
expect(list.body, body);
- 実際にcallし、結果を確認
まとめ
最近Flutterでテストコードを実装し始めて色々躓いていますが
本実装の方での書き方の注意など、テスト外の学習にも繋がりすごい楽しんでやっております。
なにかありましたら随時更新していきます。