【Flutter】mock_web_serverを用いてAPIクライアントのテストを書いてみた
IT技術
はじめに
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に追記します。
1dependencies:
2 flutter:
3 sdk: flutter
4 dio: ^4.0.6
5 json_annotation: ^4.6.0
6 retrofit: ^3.0.1+1
7
8dev_dependencies:
9 flutter_test:
10 sdk: flutter
11 build_runner: ^2.2.0
12 json_serializable: ^6.3.1
13 retrofit_generator: ^4.0.2
続いてモデル作成です。
タスクを扱うモデルを作成します。(自動生成も同時に行います。)
1import 'package:json_annotation/json_annotation.dart';
2
3part 'task.g.dart';
4
5@JsonSerializable()
6class Task {
7 const Task({
8 required this.id,
9 required this.name,
10 required this.avatar,
11 required this.createdAt,
12 });
13
14 final String id;
15 final String name;
16 final String avatar;
17 final String createdAt;
18
19 factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
20
21 Map<String, dynamic> toJson() => _$TaskToJson(this);
22}
次にAPI クライアントのクラスを作成します。(こちらも同様に自動生成を行います。)
1import 'dart:io';
2
3import 'package:dio/dio.dart';
4import 'package:mock_web_server_sample/model/group.dart';
5import 'package:mock_web_server_sample/model/task.dart';
6import 'package:retrofit/retrofit.dart';
7
8import '../request/fetch_group_request.dart';
9
10part 'rest_client.g.dart';
11
12abstract class RestClient {
13 factory RestClient(Dio dio, {String baseUrl}) = _RestClient;
14
15 @GET('/tasks')
16 Future<List<Task>> fetchTasks();
17
18 @POST("/tasks")
19 Future<Task> createTask({
20 @Body() required Task task,
21 });
22}
テストの実装
それでは上で実装したAPIクライアントのテストを書いていきます。
まずはmock_web_serverをpubspec.ymlに追加します。
1dev_dependencies:
2 flutter_test:
3 sdk: flutter
4 build_runner: ^2.2.0
5 json_serializable: ^6.3.1
6 mock_web_server: ^5.0.0-nullsafety.1 # 追加
7 retrofit_generator: ^4.0.2
次にテストファイルを作成します。
テストを書いていく前に、すべてのテストで使うことになるMockWebServerのインスタンスを返却するメソッドを追加します。
1import 'dart:convert';
2
3import 'package:flutter_test/flutter_test.dart';
4import 'package:mock_web_server/mock_web_server.dart';
5
6void main() {
7 /// MockWebServerのインスタンスを生成
8 Future<MockWebServer> _createMockWebServer({
9 required Object mockResponse,
10 }) async {
11 final server = MockWebServer();
12 addTearDown(server.shutdown);
13 await server.start();
14 // サーバー側からのレスポンスを定義
15 server.enqueue(
16 body: jsonEncode(mockResponse),
17 headers: {'Content-Type': 'application/json'},
18 );
19 return server;
20 }
21}
やっていることは下記のとおりです。
- MockWebServerのインスタンスを生成
- 各テスト終了時にMockWebServerを停止する処理を追加
- MockWebServerを起動
- server.enqueue でサーバー側からのレスポンスを定義(ここでHttpStatusCodeを設定することもできます)
- MockWebServerのインスタンスを返却
ここで作成したインスタンスを用いることでモックサーバー向けにAPIを実行でき、リクエストやレスポンスの内容をテストできるようになります。
それではテストを実際に書いていきます。
確認事項は下記です。
- リクエストURLが期待どおりであること
- ヘッダーが期待どおりであること
- リクエストに含まれている情報が期待どおりであること
1. リクエストURLが期待どおりであること
リクエストURLはtakeRequest() で取得したリクエスト情報から.uri.path で取得できます。
1 group('fetchTasks', () {
2 test('リクエスト送信時 正しいエンドポイントにリクエストが送信されていること', () async {
3 final server = await _createMockWebServer(mockResponse: [mockTask]);
4 final restClient = RestClient(Dio(BaseOptions(baseUrl: server.url)));
5 await restClient.fetchTasks();
6 final target = server.takeRequest();
7 expect(target.uri.path, '/tasks');
8 });
9 });
2. ヘッダーが期待どおりであること
ヘッダー情報はtakeRequest() で取得したリクエスト情報から.headers で取得できます。
1 group('createTask', () {
2 test('リクエスト送信時 正しいヘッダー情報が付与されていること', () async {
3 final server = await _createMockWebServer(mockResponse: mockTask);
4 final restClient = RestClient(Dio(BaseOptions(baseUrl: server.url)));
5 await restClient.createTask(task: mockTask);
6 final target = server.takeRequest();
7 expect(
8 target.headers[Headers.contentTypeHeader],
9 Headers.jsonContentType,
10 );
11 });
12 });
3. リクエストに含まれている情報が期待どおりであること
リクエストに含まれているbodyやqueryは、takeRequest() で取得したリクエスト情報から.body や.query で取得できます。
1 group('createTask', () {
2 test('リクエスト送信時 リクエストに含まれている情報が正しいこと', () async {
3 final server = await _createMockWebServer(mockResponse: mockTask);
4 final restClient = RestClient(Dio(BaseOptions(baseUrl: server.url)));
5 await restClient.createTask(task: mockTask);
6 final target = server.takeRequest();
7 expect(json.decode(target.body!), mockTask.toJson());
8 });
9 });
まとめ
これでAPIと実際に通信しなくても期待通りに動くことが担保できるようになりました!
テストを書くことで実際に疎通せずとも実装ミスに気づけたりもするので、なるべく書いていきたいですね!
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit