[Flutter 単体テスト] Mockclient type 'Null' is not a subtype of type 'Future<Response>' で躓いた

# 起きたこと
単体テスト実装中に
`Mockclient type 'Null' is not a subtype of type 'Future<Response>'`
エラーの壁に当たったが最初からやり直したら改善したため共有

## 悩んだ点
- `mockClient.get(any)` or `mockClient.get(any())` そもそもanyが使えない
- `mockClient.get(uri)`uriを同じにしてもエラーが発生
- 何をしてもmockitoの`when`でテストが上記エラーでコケる

## 改善方法
`@GenerateMocks`アノテーションを付け!
`＄ flutter pub run build_runner build`を実行し!!
MockClientクラスを自動生成することで全て解決された!!!

以下手順を記載していきます。参考になれば幸いです

## テスト対象
```dart: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`に直接書いても問題ないため、今回の内容には特に関係ないです。
```dart:ApiBaseModel
abstract class ApiBaseModel {
  Map<String, String> header() => {'Content-Type': 'application/json'};
  String domain() => 'hogehoge.co.jp';
  String path();
  Future<Response> fetch();
}
```

## テスト作成手順
1. 型作成
1. コマンド実行
1. テストコード記載

### 型作成
```dart: 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`が作成されることを確認!

### テストコード記載

```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);
    });
  });
}
```

## 簡易解説
```dart
setUp(() {
    listRepository = ListRepository(mockClient);
});
```
- repositoryにて使用するclientをmockに差し替える

```dart
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で予約

```dart
final list = await listRepository.fetch();
expect(list.body, body);
```
- 実際にcallし、結果を確認

# まとめ
最近Flutterでテストコードを実装し始めて色々躓いていますが
本実装の方での書き方の注意など、テスト外の学習にも繋がりすごい楽しんでやっております。

なにかありましたら随時更新していきます。