
【Flutter】mock_web_serverを用いてAPIクライアントのテストを書いてみた
2023.02.21
はじめに
Retrofitを用いて実装したAPIクライアントのテストの書き方を調べる際にパッケージのリポジトリを漁っていたところ、mock_web_serverというパッケージを使用するとテスト内でモックサーバーを立てることができることがわかりました!
そこで、今回はmock_web_serverを用いてRetrofitで実装したAPIクライアントのテストを書いてみます!
開発環境
開発環境は下記のとおりです。
Flutter | 3.0.5 |
使用パッケージ
retrofit | 3.0.1+1 |
mock_web_server | 5.0.0-nullsafety.1 |
dio | 4.0.6 |
json_annotation | 4.6.0 |
build_runner | 2.2.0 |
json_serializable | 6.3.1 |
retrofit_generator | 4.0.2 |
mock_web_serverを用いてAPIクライアントのテストを書く
APIクライアントの実装
それではテストの対象となるAPIクライアントを実装していきます。
本記事の目的はテストを書くことにあるので、APIは架空のものとします。
まずは使用パッケージをpubspec.ymlに追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 | dependencies: flutter: sdk: flutter dio: ^4.0.6 json_annotation: ^4.6.0 retrofit: ^3.0.1+1 dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.2.0 json_serializable: ^6.3.1 retrofit_generator: ^4.0.2 |
続いてモデル作成です。
タスクを扱うモデルを作成します。(自動生成も同時に行います。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import 'package:json_annotation/json_annotation.dart'; part 'task.g.dart'; @JsonSerializable() class Task { const Task({ required this.id, required this.name, required this.avatar, required this.createdAt, }); final String id; final String name; final String avatar; final String createdAt; factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json); Map<String, dynamic> toJson() => _$TaskToJson(this); } |
次にAPI クライアントのクラスを作成します。(こちらも同様に自動生成を行います。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import 'dart:io'; import 'package:dio/dio.dart'; import 'package:mock_web_server_sample/model/group.dart'; import 'package:mock_web_server_sample/model/task.dart'; import 'package:retrofit/retrofit.dart'; import '../request/fetch_group_request.dart'; part 'rest_client.g.dart'; abstract class RestClient { factory RestClient(Dio dio, {String baseUrl}) = _RestClient; @GET('/tasks') Future<List<Task>> fetchTasks(); @POST("/tasks") Future<Task> createTask({ @Body() required Task task, }); } |
テストの実装
それでは上で実装したAPIクライアントのテストを書いていきます。
まずはmock_web_serverをpubspec.ymlに追加します。
1 2 3 4 5 6 7 | dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.2.0 json_serializable: ^6.3.1 mock_web_server: ^5.0.0-nullsafety.1 # 追加 retrofit_generator: ^4.0.2 |
次にテストファイルを作成します。
テストを書いていく前に、すべてのテストで使うことになるMockWebServerのインスタンスを返却するメソッドを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:mock_web_server/mock_web_server.dart'; void main() { /// MockWebServerのインスタンスを生成 Future<MockWebServer> _createMockWebServer({ required Object mockResponse, }) async { final server = MockWebServer(); addTearDown(server.shutdown); await server.start(); // サーバー側からのレスポンスを定義 server.enqueue( body: jsonEncode(mockResponse), headers: {'Content-Type': 'application/json'}, ); return server; } } |
やっていることは下記のとおりです。
- MockWebServerのインスタンスを生成
- 各テスト終了時にMockWebServerを停止する処理を追加
- MockWebServerを起動
- server.enqueue でサーバー側からのレスポンスを定義(ここでHttpStatusCodeを設定することもできます)
- MockWebServerのインスタンスを返却
ここで作成したインスタンスを用いることでモックサーバー向けにAPIを実行でき、リクエストやレスポンスの内容をテストできるようになります。
それではテストを実際に書いていきます。
確認事項は下記です。
- リクエストURLが期待どおりであること
- ヘッダーが期待どおりであること
- リクエストに含まれている情報が期待どおりであること
1. リクエストURLが期待どおりであること
リクエストURLは takeRequest() で取得したリクエスト情報から .uri.path で取得できます。
1 2 3 4 5 6 7 8 9 | group('fetchTasks', () { test('リクエスト送信時 正しいエンドポイントにリクエストが送信されていること', () async { final server = await _createMockWebServer(mockResponse: [mockTask]); final restClient = RestClient(Dio(BaseOptions(baseUrl: server.url))); await restClient.fetchTasks(); final target = server.takeRequest(); expect(target.uri.path, '/tasks'); }); }); |
2. ヘッダーが期待どおりであること
ヘッダー情報は takeRequest() で取得したリクエスト情報から .headers で取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 | group('createTask', () { test('リクエスト送信時 正しいヘッダー情報が付与されていること', () async { final server = await _createMockWebServer(mockResponse: mockTask); final restClient = RestClient(Dio(BaseOptions(baseUrl: server.url))); await restClient.createTask(task: mockTask); final target = server.takeRequest(); expect( target.headers[Headers.contentTypeHeader], Headers.jsonContentType, ); }); }); |
3. リクエストに含まれている情報が期待どおりであること
リクエストに含まれているbodyやqueryは、 takeRequest() で取得したリクエスト情報から .body や .query で取得できます。
1 2 3 4 5 6 7 8 9 | group('createTask', () { test('リクエスト送信時 リクエストに含まれている情報が正しいこと', () async { final server = await _createMockWebServer(mockResponse: mockTask); final restClient = RestClient(Dio(BaseOptions(baseUrl: server.url))); await restClient.createTask(task: mockTask); final target = server.takeRequest(); expect(json.decode(target.body!), mockTask.toJson()); }); }); |
まとめ
これでAPIと実際に通信しなくても期待通りに動くことが担保できるようになりました!
テストを書くことで実際に疎通せずとも実装ミスに気づけたりもするので、なるべく書いていきたいですね!
書いた人はこんな人

- 「好きを仕事にするエンジニア集団」の(株)ライトコードです!
ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。
現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。
いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。
システム開発依頼・お見積もり大歓迎!
また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です!
インターンや新卒採用も行っております。
以下よりご応募をお待ちしております!
https://rightcode.co.jp/recruit
ライトコードの日常12月 1, 2023ライトコードクエスト〜東京オフィス歴史編〜
ITエンタメ10月 13, 2023Netflixの成功はレコメンドエンジン?
ライトコードの日常8月 30, 2023退職者の最終出社日に密着してみた!
ITエンタメ8月 3, 2023世界初の量産型ポータブルコンピュータを開発したのに倒産!?アダム・オズボーン