Flutter アプリのテスト方法

Flutter は、1 つのコードベースからネイティブにコンパイルして、モバイル、ウェブ、デスクトップの美しいアプリケーションを作成できる Google の UI ツールキットです。

この Codelab では、シンプルな Flutter アプリを作成してテストします。アプリでは、Provider パッケージを使用して状態を管理します。

学習内容

  • ウィジェット テスト フレームワークを使用してウィジェット テストを作成する方法
  • integration_test パッケージを使用してアプリの UI とパフォーマンスをテストする統合テストを作成する方法
  • 単体テストを使用してデータクラス(プロバイダ)をテストする方法

作成するアプリの概要

この Codelab では、最初に項目のリストがあるシンプルなアプリケーションを作成します。ソースコードは用意されているので、すぐにテストを開始できます。このアプリでは次の操作がサポートされます。

  • お気に入りへの項目の追加
  • お気に入りリストの表示
  • お気に入りリストからの項目の削除

アプリが完成したら、次のテストを作成します。

  • 追加操作と削除操作を検証する単体テスト
  • ホームページのウィジェットとお気に入りページのウィジェットのテスト
  • 統合テストを使用したアプリ全体の UI とパフォーマンスのテスト

Android で動作中のアプリの GIF

この Codelab で学びたいことは次のどれですか?

このトピックは初めてなので、簡単に概要を知りたい。 このトピックについてある程度は知っているが、復習したい。プロジェクトで使用するサンプルコードを確認したい。 特定の項目に関する説明を確認したい。

このラボを完了するには、Flutter SDKエディタの 2 つのソフトウェアが必要です。

この Codelab は、次のデバイスのどれを使用しても実行できます。

  • パソコンに接続され、デベロッパー モードに設定されている実機(Android または iOS)
  • iOS シミュレータ(Xcode ツールのインストールが必要です)
  • Android Emulator(Android Studio のセットアップが必要です)

新しい Flutter アプリを作成し、依存関係を更新する

この Codelab では、Flutter モバイルアプリのテストを中心に説明します。テストするアプリは、ソースファイルを切り貼りするだけで、すぐに作成できます。そのほかにも、各種のテストについて説明します。

b2f84ff91b0e1396.png 初めての Flutter アプリについての手順に沿って、テンプレート化された簡単な Flutter アプリを作成します。プロジェクトには testing_app という名前(myapp ではなく)を付けてください。このスターター アプリを修正して最終的なアプリを完成させます。

b2f84ff91b0e1396.png IDE またはエディタで、pubspec.yaml ファイルを開きます。次の new としてマークされている依存関係を追加し、ファイルを保存します(コメントを削除すると、ファイルが読みやすくなります)。

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.3
  provider: ^4.1.3            # new

dev_dependencies:
  flutter_test:
    sdk: flutter
  integration_test: ^1.0.1    # new
  test: ^1.14.4               # new
  1. IDE で [Pub get] ボタンをクリックするか、コマンドラインでプロジェクトの一番上から flutter pub get を実行します。

これがエラーになる場合は、dependencies ブロック内のインデントが、スペース(タブではなく)を使っていて、上記とまったく同じであるかを確認してください。YAML ファイルでは空白文字に意味があります。

次に、アプリを作成して、テストできるようにします。アプリには次のファイルが含まれています。

  • lib/main.dart - アプリを起動するメインファイルです。
  • lib/screens/home.dart - 項目のリストを作成します。
  • lib/screens/favorites.dart - お気に入りリストのレイアウトを作成します。
  • lib/models/favorites.dart - お気に入りリストのモデルクラスを作成します。

lib/main.dart の内容を置き換える

b2f84ff91b0e1396.png lib/main.dart の内容を次のコードに置き換えます。

lib/main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/favorites.dart';
import 'package:testing_app/screens/home.dart';

void main() {
  runApp(TestingApp());
}

class TestingApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Favorites>(
      create: (context) => Favorites(),
      child: MaterialApp(
        title: 'Testing Sample',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        routes: {
          HomePage.routeName: (context) => HomePage(),
          FavoritesPage.routeName: (context) => FavoritesPage(),
        },
        initialRoute: HomePage.routeName,
      ),
    );
  }
}

lib/screens/home.dart にホームページを追加する

b2f84ff91b0e1396.png lib ディレクトリに新しいディレクトリ screens を作成し、新しく作成したディレクトリに home.dart という新しいファイルを作成します。lib/screens/home.dart に、次のコードを追加します。

lib/screens/home.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/favorites.dart';

class HomePage extends StatelessWidget {
  static String routeName = '/';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Testing Sample'),
        actions: <Widget>[
          TextButton.icon(
            style: TextButton.styleFrom(primary: Colors.white),
            onPressed: () {
              Navigator.pushNamed(context, FavoritesPage.routeName);
            },
            icon: Icon(Icons.favorite_border),
            label: Text('Favorites'),
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: 100,
        cacheExtent: 20.0,
        padding: const EdgeInsets.symmetric(vertical: 16),
        itemBuilder: (context, index) => ItemTile(index),
      ),
    );
  }
}

class ItemTile extends StatelessWidget {
  final int itemNo;

  const ItemTile(
    this.itemNo,
  );

  @override
  Widget build(BuildContext context) {
    var favoritesList = Provider.of<Favorites>(context);

    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
        ),
        title: Text(
          'Item $itemNo',
          key: Key('text_$itemNo'),
        ),
        trailing: IconButton(
          key: Key('icon_$itemNo'),
          icon: favoritesList.items.contains(itemNo)
              ? Icon(Icons.favorite)
              : Icon(Icons.favorite_border),
          onPressed: () {
            !favoritesList.items.contains(itemNo)
                ? favoritesList.add(itemNo)
                : favoritesList.remove(itemNo);
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text(favoritesList.items.contains(itemNo)
                    ? 'Added to favorites.'
                    : 'Removed from favorites.'),
                duration: Duration(seconds: 1),
              ),
            );
          },
        ),
      ),
    );
  }
}

lib/screens/favorites.dart にお気に入りページを追加する

b2f84ff91b0e1396.png lib/screens ディレクトリに、favorites.dart という名前の新しいファイルを作成します。このファイルに次のコードを追加します。

lib/screens/favorites.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';

class FavoritesPage extends StatelessWidget {
  static String routeName = '/favorites_page';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Favorites'),
      ),
      body: Consumer<Favorites>(
        builder: (context, value, child) => ListView.builder(
          itemCount: value.items.length,
          padding: const EdgeInsets.symmetric(vertical: 16),
          itemBuilder: (context, index) => FavoriteItemTile(value.items[index]),
        ),
      ),
    );
  }
}

class FavoriteItemTile extends StatelessWidget {
  final int itemNo;

  const FavoriteItemTile(
    this.itemNo,
  );

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
        ),
        title: Text(
          'Item $itemNo',
          key: Key('favorites_text_$itemNo'),
        ),
        trailing: IconButton(
          key: Key('remove_icon_$itemNo'),
          icon: Icon(Icons.close),
          onPressed: () {
            Provider.of<Favorites>(context, listen: false).remove(itemNo);
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text('Removed from favorites.'),
                duration: Duration(seconds: 1),
              ),
            );
          },
        ),
      ),
    );
  }
}

最後に、lib/models/favorites.dartFavorites モデルを作成します。

b2f84ff91b0e1396.png 新しいディレクトリ models を作成し、そのディレクトリに favorites.dart という名前の新しいファイルを作成します。このファイルに次のコードを追加します。

lib/models/favorites.dart

import 'package:flutter/material.dart';

/// The [Favorites] class holds a list of favorite items saved by the user.
class Favorites extends ChangeNotifier {
  final List<int> _favoriteItems = [];

  List<int> get items => _favoriteItems;

  void add(int itemNo) {
    _favoriteItems.add(itemNo);
    notifyListeners();
  }

  void remove(int itemNo) {
    _favoriteItems.remove(itemNo);
    notifyListeners();
  }
}

これでアプリは完成しましたが、テストされていません。

b2f84ff91b0e1396.png エディタで実行アイコン 6869d41b089cc745.png をクリックしてアプリを実行します。初めてアプリを実行する場合、時間がかかることがありますが、以降のステップでは高速化されます。次のスクリーンショットのような画面が表示されます。

be938199b599b605.png

アプリに項目のリストが表示されます。いずれかの行にあるハート型のアイコンをタップして、ハートを塗りつぶされた状態にし、その項目をお気に入りリストに追加します。AppBar の [Favorites] ボタンをクリックすると、お気に入りのリストを含む、2 番目の画面が表示されます。

これで、アプリのテスト準備が整いました。次のステップでテストを開始します。

favorites モデルの単体テストから始めます。単体テストとは、どんなものでしょうか。単体テストでは、ソフトウェアの各単位(多くの場合、関数)で目的のタスクが正しく実行されることを確認します。

Flutter アプリ内のすべてのテストファイル(統合テストを除く)は test ディレクトリに配置されます。

test/widget_test.dart を削除する

b2f84ff91b0e1396.png テストを開始する前に、widget_test.dart ファイルを削除します。これから独自のテストファイルを追加します。

新しいテストファイルを作成する

まず、Favorites モデル内の add() メソッドのテストとして、新しい項目がリストに追加され、リストの変更が反映されていることを確認します。慣例として、test ディレクトリのディレクトリ構造は lib ディレクトリと同じにして、Dart ファイルの名前は同じものに _test を付加します。

b2f84ff91b0e1396.png test ディレクトリに models ディレクトリを作成します。この新しいディレクトリに、次の内容の favourites_test.dart ファイルを作成します。

test/models/favorites_test.dart

import 'package:test/test.dart';
import 'package:testing_app/models/favorites.dart';

void main() {
  group('App Provider Tests', () {
    var favorites = Favorites();

    test('A new item should be added', () {
      var number = 35;
      favorites.add(number);
      expect(favorites.items.contains(number), true);
    });
  });
}

Flutter テスト フレームワークでは、グループ内で互いに関連した類似のテストをまとめることができます。1 つのテストファイルに複数のグループを含めると、/lib ディレクトリ内の対応するファイルの別々の部分をテストできます。

test() メソッドは、位置に意味のある 2 つのパラメータを取ります。テストの description と、実際のテストを記述する callback です。

b2f84ff91b0e1396.png リストからの項目の削除をテストします。次のテストをコピーして、同じテストグループに貼り付けます。テストファイルに次のコードを追加します。

test/models/favorites_test.dart

test('An item should be removed', () {
  var number = 45;
  favorites.add(number);
  expect(favorites.items.contains(number), true);
  favorites.remove(number);
  expect(favorites.items.contains(number), false);
});

テストを実施する

b2f84ff91b0e1396.png エミュレータやデバイスでアプリを実行している場合は、続行する前にアプリを閉じます。

b2f84ff91b0e1396.png コマンドラインでプロジェクトのルート ディレクトリに移動し、次のコマンドを入力します。

$ flutter test test/models/favorites_test.dart

すべてが問題なく動作した場合は、次のようなメッセージが表示されます。

00:06 +2: All tests passed!

完全なテストファイルは、こちらのリンク(test/models/favorites_test.dart)から入手できます。

単体テストの詳細については、「単体テストの概要」をご覧ください。

このステップでは、ウィジェット テストを実施します。ウィジェット テストは Flutter に固有のテストであり、選択されたウィジェットを個別にテストできます。このステップでは、画面(HomePageFavoritesPage)を別々にテストします。

ウィジェット テストでは、test() 関数の代わりに testWidget() 関数を使用します。この関数も、description,callback の 2 つのパラメータを取ります。ただし、コールバックは引数として WidgetTester を取ります。

ウィジェット テストでは TestFlutterWidgetsBinding を使用します。このクラスは、実行中のアプリと同じリソース(画面サイズ、アニメーションのスケジュールに関する情報など)を提供しますが、実際のアプリは使用しません。代わりに、仮想環境を使用してウィジェットを実行し、測定などを行い、結果をテストします。pumpWidget は、完全なアプリケーションで行うのと同じように、特定のウィジェットを用意して測定するようにフレームワークに指示することで、この処理を開始します。

ウィジェット テスト フレームワークには、ウィジェットを探すためのファインダー(text()byType()byIcon() など)や、結果を確認するためのマッチャーが用意されています。

まず、HomePage ウィジェットをテストします。

新しいテストファイルを作成する

最初のテストでは、HomePage のスクロールが正しく動作するかどうかを確認します。

b2f84ff91b0e1396.png test ディレクトリに新しいファイルを作成し、home_test.dart という名前を付けます。新しく作成したファイルに、次のコードを追加します。

test/home_test.dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/home.dart';

Widget createHomeScreen() => ChangeNotifierProvider<Favorites>(
      create: (context) => Favorites(),
      child: MaterialApp(
        home: HomePage(),
      ),
    );

void main() {
  group('Home Page Widget Tests', () {
    testWidgets('Testing Scrolling', (tester) async {
      await tester.pumpWidget(createHomeScreen());
      expect(find.text('Item 0'), findsOneWidget);
      await tester.fling(find.byType(ListView), Offset(0, -200), 3000);
      await tester.pumpAndSettle();
      expect(find.text('Item 0'), findsNothing);
    });
  });
}

createHomeScreen() 関数では、テストするウィジェットが MaterialApp に作成され、ChangeNotifierProvider でラップされます。HomePage ウィジェットが両方のウィジェットを継承し、それらが提供するデータにアクセスできるように、両方のウィジェットはウィジェット ツリーで上にある必要があります。この関数は、パラメータとして pumpWidget() 関数に渡されます。

次に、画面にレンダリングされた ListView をフレームワークが検出できるかどうかをテストします。

b2f84ff91b0e1396.png 次のコード スニペットを home_test.dart に追加します。

test/home_test.dart

group('Home Page Widget Tests', () {

  // BEGINNING OF NEW CONTENT
  testWidgets('Testing if ListView shows up', (tester) async {
    await tester.pumpWidget(createHomeScreen());
    expect(find.byType(ListView), findsOneWidget);
  });
  // END OF NEW CONTENT

  testWidgets('Testing Scrolling', (tester) async {
    await tester.pumpWidget(createHomeScreen());
    expect(find.text('Item 0'), findsOneWidget);
    await tester.fling(find.byType(ListView), Offset(0, -200), 3000);
    await tester.pumpAndSettle();
    expect(find.text('Item 0'), findsNothing);
  });
});

テストを実施する

ウィジェット テストは単体テストと同じように実行できますが、デバイスまたはエミュレータを使用するとテストの実行を監視できます。ホット リスタートも可能になります。

b2f84ff91b0e1396.png デバイスを接続するか、エミュレータを起動します。

b2f84ff91b0e1396.png コマンドラインからプロジェクトのルート ディレクトリに移動し、次のコマンドを入力します。

$ flutter run test/home_test.dart

すべてが問題なく動作した場合は、次のような出力が表示されます。

Launching test/home_test.dart on Mi A3 in debug mode...
Running Gradle task 'assembleDebug'...
Running Gradle task 'assembleDebug'... Done                        62.7s
✓ Built build/app/outputs/flutter-apk/app-debug.apk.
Installing build/app/outputs/flutter-apk/app.apk...                 5.8s
Waiting for Mi A3 to report its views...                            16ms
I/flutter ( 1616): 00:00 +0: Home Page Widget Tests Testing if ListView shows up
Syncing files to device Mi A3...
I/flutter ( 1616): 00:02 +1: Home Page Widget Tests Testing Scrolling
Syncing files to device Mi A3...                                                 4,008ms (!)

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h Repeat this help message.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
An Observatory debugger and profiler on Mi A3 is available at:
http://127.0.0.1:40433/KOsGesHSxR8=/
I/flutter ( 1616): 00:00 +0: Home Page Widget Tests Testing if ListView shows up
I/flutter ( 1616): 00:02 +1: Home Page Widget Tests Testing Scrolling
I/flutter ( 1616): 00:09 +3: All tests passed!

次に、テストファイルを変更して Shift + R を入力し、アプリをホット リスタートしてすべてのテストを再実行します。

b2f84ff91b0e1396.png HomePage ウィジェットをテストするグループにテストを追加します。次のテストをファイルにコピーします。

test/home_test.dart

testWidgets('Testing IconButtons', (tester) async {
  await tester.pumpWidget(createHomeScreen());
  expect(find.byIcon(Icons.favorite), findsNothing);
  await tester.tap(find.byIcon(Icons.favorite_border).first);
  await tester.pumpAndSettle(Duration(seconds: 1));
  expect(find.text('Added to favorites.'), findsOneWidget);
  expect(find.byIcon(Icons.favorite), findsWidgets);
  await tester.tap(find.byIcon(Icons.favorite).first);
  await tester.pumpAndSettle(Duration(seconds: 1));
  expect(find.text('Removed from favorites.'), findsOneWidget);
  expect(find.byIcon(Icons.favorite), findsNothing);
});

このテストでは、IconButton をタップすると Icons.favorite_border(塗りつぶされていないハート)から Icons.favorite(塗りつぶされているハート)に変わり、もう一度タップすると Icons.favorite_border に戻ることを確認します。

b2f84ff91b0e1396.png Shift + R を入力します。これにより、アプリがホット リスタートされ、すべてのテストが再実行されます。

完全なテストファイルは、こちらのリンク(test/home_test.dart.)から入手できます。

b2f84ff91b0e1396.png 次のコードを使用し、同じ処理で FavoritesPage をテストします。これを同じ手順で実行します。

test/favorites_test.dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/favorites.dart';

Favorites favoritesList;

Widget createFavoritesScreen() => ChangeNotifierProvider<Favorites>(
      create: (context) {
        favoritesList = Favorites();
        return favoritesList;
      },
      child: MaterialApp(
        home: FavoritesPage(),
      ),
    );

void addItems() {
  for (var i = 0; i < 10; i += 2) {
    favoritesList.add(i);
  }
}

void main() {
  group('Favorites Page Widget Tests', () {
    testWidgets('Test if ListView shows up', (tester) async {
      await tester.pumpWidget(createFavoritesScreen());
      addItems();
      await tester.pumpAndSettle();
      expect(find.byType(ListView), findsOneWidget);
    });

    testWidgets('Testing Remove Button', (tester) async {
      await tester.pumpWidget(createFavoritesScreen());
      addItems();
      await tester.pumpAndSettle();
      var totalItems = tester.widgetList(find.byIcon(Icons.close)).length;
      await tester.tap(find.byIcon(Icons.close).first);
      await tester.pumpAndSettle();
      expect(tester.widgetList(find.byIcon(Icons.close)).length,
          lessThan(totalItems));
      expect(find.text('Removed from favorites.'), findsOneWidget);
    });
  });
}

このテストでは、閉じる(削除)ボタンが押されたときに項目が消えるかどうかを確認します。

ウィジェット テストの詳細については、以下をご覧ください。

統合テストは、アプリの各部分がどのように連携するかをテストするために使用されます。integration_test パッケージは Flutter での統合テストに使用されます。これは、Flutter 版の Selenium WebDriver(汎用ウェブ)、Protrator(Angular)、Espresso(Android)、Earl Gray(iOS)です。パッケージは、内部で flutter_driver を使用してデバイス上でテストを実行します。

アプリをインストルメント化する

統合テストを作成するには、最初にアプリをインストルメント化する必要があります。アプリのインストルメント化とは、自動テストの作成と実行のためにドライバが GUI と関数にアクセスできるようにアプリを構成することを意味します。統合テストは、integration_test というディレクトリに配置されます。このステップでは、統合テスト用の次のファイルを追加します。

  • integration_test/driver.dart - アプリをインストルメント化します。
  • integration_test/app_test.dart - アプリで実際のテストを実行します。

b2f84ff91b0e1396.png プロジェクトのルート ディレクトリに integration_test という名前のディレクトリを作成します。新しく作成したディレクトリに driver.dart ファイルを作成し、次のコードを追加します。

integration_test/driver.dart

import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();

このコードは、統合テストドライバを有効にしてから、テストが実行されるのを待ちます。応答データは、テストの実行後に integration_response_data.json という名前のファイルに保存されます。

テストを作成する

b2f84ff91b0e1396.png 新しいファイルを作成して app_test.dart という名前を付けます。

integration_test/app_test.dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:testing_app/main.dart';

void main() {
  group('Testing App Performance Tests', () {
    final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized()
        as IntegrationTestWidgetsFlutterBinding;

    binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
  });
}

ensureInitialized() 関数は、統合テストドライバが初期化されているかどうかを確認し、必要に応じて再度初期化します。大量のアニメーションがある状況をテストする場合、framePolicyLiveTestWidgetsFlutterBindingFramePolicy 列挙型の fullyLive に設定するのが最適です。

次に、アプリのスクロール性能をテストし、それを watchPerformance() 関数を使用して記録します。

b2f84ff91b0e1396.png さきほど作成したテストグループに次のコードを貼り付けます。

integration_test/app_test.dart

testWidgets('Scrolling test', (tester) async {
  await tester.pumpWidget(TestingApp());

  final listFinder = find.byType(ListView);

  await binding.watchPerformance(() async {
    await tester.fling(listFinder, Offset(0, -500), 10000);
    await tester.pumpAndSettle();

    await tester.fling(listFinder, Offset(0, 500), 10000);
    await tester.pumpAndSettle();
  }, reportKey: 'scrolling_summary');
});

このテストでは、項目のリストを高速に最後までスクロールしてから、スクロールで元に戻します。watchPerformance() 関数がアクションを記録し、タイムライン サマリーを生成します。サマリーは、driver.dart ファイル内のテストドライバに応答データとして返されます。

次に、add 操作と remove 操作をテストします。

b2f84ff91b0e1396.png 次のテストを同じグループに貼り付けます。

integration_test/app_test.dart

testWidgets('Favorites operations test', (tester) async {
  await tester.pumpWidget(TestingApp());

  final iconKeys = [
    'icon_0',
    'icon_1',
    'icon_2',
  ];

  for (var icon in iconKeys) {
    await tester.tap(find.byKey(ValueKey(icon)));
    await tester.pumpAndSettle(Duration(seconds: 1));

    expect(find.text('Added to favorites.'), findsOneWidget);
  }

  await tester.tap(find.text('Favorites'));
  await tester.pumpAndSettle();

  final removeIconKeys = [
    'remove_icon_0',
    'remove_icon_1',
    'remove_icon_2',
  ];

  for (final iconKey in removeIconKeys) {
    await tester.tap(find.byKey(ValueKey(iconKey)));
    await tester.pumpAndSettle(Duration(seconds: 1));

    expect(find.text('Removed from favorites.'), findsOneWidget);
  }
});

テストを実施する

b2f84ff91b0e1396.png デバイスを接続するか、エミュレータを起動します。

b2f84ff91b0e1396.png コマンドラインでプロジェクトのルート ディレクトリに移動し、次のコマンドを入力します。

$ flutter drive --driver integration_test/driver.dart --target integration_test/app_test.dart --profile

すべてが問題なく動作した場合は、次のような出力が表示されます。

Running "flutter pub get" in step_07...                            930ms
Running Gradle task 'assembleProfile'...
Running Gradle task 'assembleProfile'... Done                      31.3s
✓ Built build/app/outputs/flutter-apk/app-profile.apk (11.3MB).
Installing build/app/outputs/flutter-apk/app.apk...                277ms
VMServiceFlutterDriver: Connecting to Flutter application at http://127.0.0.1:62862/K6QKjUNab8c=/
VMServiceFlutterDriver: Isolate found with number: 1935648057883071
VMServiceFlutterDriver: Isolate is paused at start.
VMServiceFlutterDriver: Attempting to resume isolate
I/flutter (24385): 00:00 +0: Testing App Performance Tests Scrolling test
VMServiceFlutterDriver: Connected to Flutter application.
I/flutter (24385): 00:08 +1: Testing App Performance Tests Favorites operations
test
I/flutter (24385): 00:17 +2: Testing App Performance Tests (tearDownAll)
I/flutter (24385): 00:17 +3: All tests passed!
All tests passed.

テストが正常に完了した後には、プロジェクトのルートにある build ディレクトリに integration_response_data.json という 1 つのファイルがあるはずです。これには、実行中にテストから戻された応答データ(この場合は scrolling_summary)が入っています。テキスト エディタでファイルを開いて、情報を確認してください。詳細設定を行うことで、テストを実行するたびにサマリーを保存し、結果のグラフを作成することもできます。

完全なテストファイルは、こちらのリンク(integration_test/app_test.dart.)から入手できます。

Flutter ドライバ(統合)テストの詳細については、以下をご覧ください。

この Codelab を修了し、Flutter アプリをテストするためのさまざまな方法を学びました。

学習した内容

  • ウィジェット テスト フレームワークを使用してウィジェットをテストする方法
  • 統合テストを使用してアプリの UI をテストする方法
  • 統合テストを使用してアプリの性能をテストする方法
  • 単体テストを使用してプロバイダをテストする方法

Flutter でのテストについて詳しくは、以下をご覧ください。