テスト基本 - 1m-llc/Flutter-KtoK GitHub Wiki
※各コードはFlutter公式サイトより引用しています。
- Unit Testsとレシピ
実行コマンド例
$ flutter test test/widget_test.dart
1.単一機能のテスト
■説明
単体テストは、単一の関数、メソッド、またはクラスをテストします。単体テストの目的は、さまざまな条件下での論理ユニットの正しさを検証することです。テスト対象のユニットの外部依存関係は、一般的にモックアウトされています。単体テストは通常、ディスクからの読み取りやディスクへの書き込み、画面へのレンダリング、テストを実行しているプロセスの外部からのユーザーアクションの受信は行いません。
例:
class Counter {
int value = 0;
void increment() => value++;
void decrement() => value--;
}
// Import the test package and Counter class
import 'package:test/test.dart';
import 'package:counter_app/counter.dart';
void main() {
test('Counter value should be incremented', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
}
2.モックを使ったテスト
■説明
実際のWebサービスやデータベースの依存関係を「模擬」できます。モックを使用すると、Webサービスまたはデータベースをエミュレートできます。一般的にはクラスの代替実装を作成することで依存関係を模倣できます。これらのモックを手動で記述するか、Mockitoパッケージを利用できます。
https://pub.dev/packages/mockito
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) {
// If the call to the server was successful, parse the JSON.
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:http/http.dart' as http;
// Create a MockClient using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockClient extends Mock implements http.Client {}
main() {
group('fetchPost', () {
test('returns a Post if the http call completes successfully', () async {
final client = MockClient();
// Use Mockito to return a successful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
expect(await fetchPost(client), isA<Post>());
});
test('throws an exception if the http call completes with an error', () {
final client = MockClient();
// Use Mockito to return an unsuccessful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('Not Found', 404));
expect(fetchPost(client), throwsException);
});
});
}
- Widget testsとレシピ
■説明
ウィジェットテストでは、単一のウィジェットをテストします。ウィジェットテストの目的は、ウィジェットのUIが期待どおりに見え、相互作用することを確認することです。ウィジェットのテストには複数のクラスが含まれ、適切なウィジェットライフサイクルコンテキストを提供するテスト環境が必要です。たとえば、テストされるウィジェットは、ユーザーのアクションとイベントを受け取って応答し、レイアウトを実行し、子ウィジェットをインスタンス化できる必要があります。したがって、ウィジェットテストは単体テストよりも包括的です。
class MyWidget extends StatelessWidget {
final String title;
final String message;
const MyWidget({
Key key,
@required this.title,
@required this.message,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
),
);
}
}
タイトルに「T」、メッセージに「M」を表示するMyWidgetインスタンスを作成します。
void main() {
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
});
}
特定のテキストを含むウィジェットを見つける際にfind.text()メソッドを使用します。
testWidgets('finds a Text widget', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Text('H'),
),
));
expect(find.text('H'), findsOneWidget);
});
- Integration testsとレシピ
■説明
インテグレーションテストでは、アプリ全体またはアプリの大部分をテストします。インテグレーションテストの目的は、テスト対象のすべてのウィジェットとサービスが期待どおりに連携することを確認することです。さらに、統合テストを使用して、アプリのパフォーマンスを確認できます。通常、統合テストは、実際のデバイスまたはiOSシミュレーターやAndroidエミュレーターなどのOSエミュレーターで実行されます。
インテグレーションテストでは「test_driver」というフォルダに作成していきます。
test
test_driver
∟ app_test.dart
また以下を行います。
- flutter driver機能をONにする
- メインアプリを起動する
import 'package:flutter_driver/driver_extension.dart';
import 'package:sample/main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}