📝

【Flutter/Dart】 testについて

2022/04/24に公開

概要

昨今、品質の担保や向上が注目される中でシステムの各分野においてもテストの自動化が注目さている。
今まではテスト仕様書を作成し、それっを目視でテストするそんなものもあったは、回数を重ねれば同じテストをしても精度が低い場面に直面することもあるだろう。
なので最近はテストはコーディングするのが主流になっている。

test

  • 動画

https://youtu.be/bj-oMYyLZEY

  • 方法について
    主には以下の3種類がある。
  1. Integration
  2. Unit
  3. Widget

https://docs.flutter.dev/cookbook/testing

やってみた。

共通部分

  • test用のライブラリの取り込み
    多分、書いてあるが、 以下を記述してflutter pub getを叩く
pubspec.yaml
dev_dependencies:
  integration_test:
    sdk: flutter
  flutter_test:
    sdk: flutter
  • test用のフォルダとdartファイルを準備する。
your_app/
  lib/
    main.dart
  integration_test/
    app_test.dart
  test/
    unit/
      unit_test.dart
    widget/
      widget_test.dart

Integrationテストの場合

integration_test.dart
import 'package:flutter_test/flutter_test.dart'; //testコードはこれをimport
import 'package:integration_test/integration_test.dart'; //インテグレーションtestコードはこれをimport

import 'package:introduction/main.dart' as app; // as appでrunAppを記述したdartファイルを呼び出す。

void main() {
  // インテグレーションテストの場合はこれを1行目に書く
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

    // テストをグループ化するコード
  group('end-to-end test', () {
  // Widgetテストのコード
    testWidgets('tap on the floating action button, verify counter',
        (WidgetTester tester) async {
      // ここでrunAppを起動
      app.main();
      // pumpAndSettleで画面を起動を待つ
      await tester.pumpAndSettle();

      // 初期値を確認
      expect(find.text('0'), findsOneWidget);

      // Widgetをtooltipをキーに見つける。…①
      final Finder fab = find.byTooltip('Increment');

      // ①で見つけたWidgetをタップするコード…②
      await tester.tap(fab);

      // ②でタップした結果を待つ、ロジック
      await tester.pumpAndSettle();

      // ボタン押下で内容が変化したかを確認するロジック
      expect(find.text('1'), findsOneWidget);
    });
    
  });
}

【解説】

  • Finder
    Widgetをみつけて、そのWidgetに対してアクションを起こしたり、Widgetが格納している情報を取り出す。

  • expect
    expect(A, B)A:Finderで指定した情報をとりだす。B:取り出した情報にマッチするじょうけんがいくつ存在しているかを指定する。

    findsOneWidget // Finderで指定した条件のWidgetが一つだけ存在している場合。
    findsNothing // Finderで指定した条件のWidgetが存在していない。
    findsWidgets // Finderで指定した条件のWidgetが複数存在している場合。
    findsNWidgets(n) // Finderで指定した条件のWidgetがn個存在している場合。
    

unitテストの場合

unit_test.dart
import 'package:flutter_test/flutter_test.dart';

void main() {
  test('unit Test', () {
    var test = 5;
    expect(test, 5);
  });
}

まぁ、integrationテストでほとんど触れているので特に何もいうことはないが、
controller,service,providerなどのテストで使用できる。

widgetテストの場合

widget_test.dart
import 'package:flutter_test/flutter_test.dart';

testWidgets('NoteTest', (WidgetTester tester) async {
    await tester.pumpWidget(
      MaterialApp(
        onGenerateRoute: (settings) {
          if (settings.name == '/') {
            return MaterialPageRoute(builder: (_) => const NoteListPage());
          } else if (settings.name == '/edit') {
            final arguments = settings.arguments as String?;
            return MaterialPageRoute(
              builder: (_) => NoteEditPage(note: arguments ?? ""),
            );
          }

          throw Exception('Route not found');
        },
      ),
    );

    expect(find.text('There\'s nothing to show'), findsOneWidget);

    await tester.tap(find.byKey(const Key('add_note')));
    await tester.pumpAndSettle();

    expect(find.byKey(const Key('edit_text')), findsOneWidget);

    await tester.enterText(find.byKey(const Key('edit_text')), 'ゴゴゴゴゴ・・・。');

    await tester.tap(find.byKey(const Key('commit_button')));
    await tester.pumpAndSettle();

    expect(find.text('ゴゴゴゴゴ・・・。'), findsOneWidget);
    
    ・・・
  });

Widgetをガチの単体テストでやりたい場合は、tester.pumpWidgetMaterialAppを包んで中にテストしたいWidgetを入れます。あとはインテグレーションテストと同じ方法でいけます。

非同期のテストに関して

ライブラリを追加します

pubspec.yaml
dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^1.0.0
  mockito: #これと
  build_runner:  #これを追加する。
ライブラリ 内容
mockito mockitoで非同期実行用クラスを自動生成します。
build_runner mockitoをビルドするために必要です。

サンプルコード

  1. まずはこの状態でビルドします
async_test.dart
([テストしたいClass])
void main() {
}
  1. 上記、コードを作成し、ワークディレクトリで以下のコマンドを叩きます。
$ flutter pub pub run build_runner build

すると、MockテストしたいClassというクラスができます。

  1. これを使ってテストコードを書きます。
async_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:sample/apis/MockビルドしたいClass.dart';
import 'package:sample/models/return_mock.dart';
import 'api_test.mocks.dart';

([テストしたいClass])
void main() {
  var repository = MockテストしたいClass();

  test('Testing cat sound method.', () async {
    final returnMock = ReturnMock(
      description: '',
    );
    
    when(repository.search('****')).thenAnswer((_) async => returnMock);
    expect(await repository.search('****'), returnMock);
    verify(repository.search('****'));
  });
}

こんな感じでMockitoを使うことで非同期処理のテストコードを記述することも可能です。

Finderについて

これは、Widgetを見つけるためのファンクションだが、見つける手段が山のようにあるので以下で参照してほしい。主に使うのはbyKey(const Key('キーの名前'))である

https://github.com/flutter/flutter/blob/master/packages/flutter_test/lib/src/finders.dart

Keyについて

Keyはテストにおいては、Widgetを見つけるためにWidgetに設定することができるKey値である。
だいたいどのWidgetにも設定できる。

Text(
  'Test',
  key: const ValueKey('sample_key'),
),

Keyの種類

  • GlobalKey
    1. GlobalKey
  • LocalKey
    1. UniqueKey
    2. ValueKey
    3. ObjectKey
    4. PageStorageKey

詳しくは動画見てチェック

https://youtu.be/kn0EOS-ZiIc

最後に

今回は長くなったのでこの辺にしておくがもっともっとTestは奥が深いので、実装をもっとして深めていければと思う。

Discussion