Testing Flutter Appを写経しました。
この記事では、後述するUnit Test
についてまとめます。
Flutterの自動テスト
Flutterでの自動テストは、以下のように分類できます。
-
Unit Test
: 関数やメソッド、クラスのロジックが正しいか検証します。 -
Widget Test
: Widgetが期待通りに動作するか、期待通りのUIを実現できているか検証します。 -
Integration Test
: アプリそのものや機能の大部分をテストします。Widgetやサービスを結合させて動作した時、期待通りに動作するか検証します
Unit Test
はコストが低く、高速にテストできます。
Unit Test
ここでは、flutter_test
を使ったテストの書き方を説明します。
Unit Testの効果的な書き方や考え方は別な記事を参考にしましょう。
flutter_test
パッケージを導入する
flutter_test
を導入するため、以下のようにpubspec.yamlに追記します。
dev_dependencies:
flutter_test:
sdk: flutter
テストを書いてみる
今回はCounter
というクラスにテストを書きます。
lib/counter.dart
を作成しましょう。
class Counter {
int value = 0;
void increment() => value++;
void decrement() => value--;
}
test/counter_test.dart
を追加し、テスト用のコードを書きます。
test()
でテストケースを定義し、expect()
で期待通りの結果が得られているか検証します。
import 'package:flutter_test/flutter_test.dart';
import 'package:unit_testing/counter.dart';
void main() {
// group()でテストをグルーピングできる
group('Counter', () {
test('Counterの初期値は0', () {
expect(Counter().value, 0);
});
test('increment()を呼び出すと1増える', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
test('decrement()を呼び出すと1減る', () {
final counter = Counter();
counter.decrement();
expect(counter.value, -1);
});
});
}
このサンプルでは簡単な例ですが、他にも非同期のテストやMatcher
を使った検証もできます。
リファレンスを読みましょう。
Mockito
の導入
テスト対象の振る舞いが別なクラスに依存している場合、モックライブラリのMockito
を使ってモックしましょう。
今回はhttpで通信した結果をモックしてみます。
例によってpubspec.yamlに追記します。
dependencies:
flutter:
sdk: flutter
http:
dev_dependencies:
flutter_test:
sdk: flutter
mockito:
lib/main.dart
に、こんな感じでHTTPで通信してJSONをパースする処理を書きます。
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class Post {
dynamic data;
Post.fromJson(this.data);
}
Future<Post> fetchPost(http.Client client) async {
final response =
await client.get('https://jsonplaceholder.typicode.com/posts/1');
if (response.statusCode == 200) {
return Post.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load post');
}
}
今回のクラスをテストするには、ネットワーク環境を準備したり通信先のサーバーを準備したりサーバーが狙い通りに動作するように準備したりと、準備がとても大変です。
なので、Mockito
を使ってサーバーとの通信が成功したように振る舞わせてテストしましょう。
import 'package:flutter_test/flutter_test.dart';
import 'package:first_mockito/main.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/mockito.dart';
// モック対象のクラスを宣言します。
class MockClient extends Mock implements http.Client {}
void main() {
group('fetchPost', () {
test('returns a Post if the http call completes successfully', () async {
// モックを生成します。
final client = MockClient();
// client.get()を呼び出した時に、どういうResponseを返すかモックします。
// このテストでは200を返すケースをモックしてみます。
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('{"title" : "Test"}', 200));
expect(isInstanceOf<Post>(fetchPost(client)), isTrue);
});
test('throws an exception if the http call completes with an error', () {
final client = MockClient();
// このテストでは404を返すケースをモックしてみます。
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('Not Found', 404));
expect(fetchPost(client), throwsException);
});
});
}
Mockito
はモックだけでなく、スタブ・Fake(Partial Mock)の使い方も可能です。
公式のドキュメントを読んでみましょう。