2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

起きたこと

単体テスト実装中に
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クラスを自動生成することで全て解決された!!!

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

テスト対象

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

テスト作成手順

  1. 型作成
  2. コマンド実行
  3. テストコード記載

型作成

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でテストコードを実装し始めて色々躓いていますが
本実装の方での書き方の注意など、テスト外の学習にも繋がりすごい楽しんでやっております。

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

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?