iOS Android

Flutterでテストコードを書こう! 単体テスト・Widgetテスト・インテグレーションテスト

投稿日:2020年3月18日 更新日:

はじめに

Flutterでは、テストコードを実装しやすい環境が整っています。この記事では、単体テスト・Widgetテスト・インテグレーションの実装方法を紹介します。

単体テスト

Dartの test パッケージを利用したテスト方法を紹介します。

testパッケージの利用

pubspec.yamltest を読み込むようにします。

test は、使用中のFlutter SDKに含まれる、 flutter_test が依存しているため、 test の最新バージョンを使用してしまうと、Packages getに失敗してしまうことがあります。
その問題を解決するため、バージョン指定には any を使用しています。
ただし、実際には、バージョンを指定すべきだと思うため、 any でPackages getが成功したら、 pubspec.lock で実際にインストールされたバージョンを確認し、 pubspec.yaml に反映させるべきだと思います。

テストの書き方

まずは、このような簡単なクラスをテストしてみます。

テストコードは、 test/ 配下にDartファイルを作成し、実装します。

テストケースごとに、 test() 関数を呼び出します。第一引数はテスト名、第二引数がテストの内容となります。

テスト結果の検証は expect() 関数を使用します。第一引数は検証対象、第二引数は期待値となります。もし、期待値と異なれば、テストは失敗となります。

テストの実行

テストを実行するには、作成したテストコードのDartファイルを右クリックし、 Run tests in プロジェクト名 を選択して実行できます。
test() 単位でテストするには、テストコードの行番号の左側にあるRunボタンをクリックすると、そのテストのみが実行されます。
コマンドで実行するには次のようにします。

モック化

例えば、テストコードを書こうとしたとき、テスト対象クラスが、Webサービスにアクセスするクラスに依存している場合、テストは自動化されても、実行環境を整える手間がかかります。
そこで、そのようなクラスはモック化(模倣)し、テストに必要なデータを返却するようにすることで、本来テストしたい領域のみを単体でテストすることができます。

mockitoの導入

pubspec.ymlに以下を追加し、Packages getします。

バージョンは、mockitoのページで最新版を確認してください。

メソッドのモック化

例として、非同期メソッドをモック化してみます。実際には、通信処理や、重い処理の実行をモック化する際に、使用できます。
こちらのサンプルコードをモック化します。

次に、モック化したテストコードです。

まず、(1)のところでモック用のクラスを宣言します。 Mock クラスを継承し、モック化したいクラスをインターフェースとして設定します。

ちなみに、Javaではここで IGreeting と命名されるようなインターフェイスを定義しておき、 Greeting クラスと MockGreeting の両方に実装させることになると思います。
しかし、Dartではモック化したいクラスをそのままインターフェイスとすることができます。
なぜなら、Dartでは暗黙的インターフェイスという仕様があり、全てのクラスはインターフェイスとして利用できるためです。
その場合、クラス内の全てのメソッドやプロパティが実装必須となります。(privateなものは除きます。)

次に、モックの実装です。
(2)の部分でモックをインスタンス化し、(3)以降のところでモックを実装していきます。
when() メソッドでモック対象を指定し、 thenAnswer() メソッドでメソッドの内容を書き換え、返り値を設定します。
今回は、 Future<String> が戻り値なので、 Future.value() で値を返却しています。

その他のモック化

thenAnswer() 以外の、モック化の方法を紹介します。
単純に返り値だけを変更したいのであれば、 thenReturn() メソッドが使用できます。ただし、 Future クラスのモック化には使用できないので注意してください。

Widgetテスト

Flutterは、Widget単位でのテストもサポートしています。Widgetの状態遷移をテストコードで実装し、結果を検証することで、テストすることができます。

flutter_testの導入

flutter_testはプロジェクト作成時から入っていますが、入っていない場合はpubspec.yamlに以下を追加します。

テストの書き方

「単体テスト」で使用したGreetingクラスを使用して、Widgetを実装します。

名前を入力して、「あいさつする」ボタンをタップすると、テキストが更新されるシンプルなWidgetです。
このWidgetのテストコードのサンプルです。

テストコードの中身を見ていきましょう。

Widgetの取得方法

Widgetを取得する代表的な方法を紹介します。

find.text()メソッド

Textや、Textを内包しているWidget(ボタンなど)を取得することができます。

find.byKey()メソッド

取得したいWidgetに設定した Key で、Widgetを特定します。

Widgetの操作方法

findで取得したWidgetを操作する代表的な方法を紹介します。

tap()メソッド

引数に渡されたボタンをタップします。

enterText()メソッド

第1引数で渡されたWidgetに、第2引数のテキストを入力します。

画面が変化した後に呼ぶpump()メソッド

ボタンアクション等によってWidgetツリーを更新した直後に expect() メソッドで検証する場合、ツリーが更新されるまで待つ必要があります。
そのような時は、 pump() メソッドを挟む事で、処理待ちを行うことができます。

検証

単体テストの時と同じように expect() メソッドを使用します。

第1引数に検証したいWidget、第2引数に取得したWidgetを検証する方法を指定します。検証方法は次の4つです。

  • findsOneWidget
    1つのWidgetが見つかること
  • findsNothing
    Widgetが見つからないこと
  • findsWidgets
    1つ以上のWidgetが見つかること
  • findsNWidgets
    特定の数のWidgetが見つかること

テストの実行

単体テストと同じ方法で実行できます。
コマンドで実行するには次のようにします。

インテグレーションテスト

インテグレーションテストでは実機でアプリを実際に動作させ、自動的に操作・検証することで、アプリ全体を網羅的にテストします。
単体テストより実行に時間はかかりますが、実機で通しテストを行う点がポイントです。
「Widgetテスト」で使用したページを使用して、インテグレーションテストを行なっていきます。

flutter_driverの導入

pubspec.yamlに以下を追加します。

testパッケージに依存しているため、testパッケージも使用できるようにします。

テストの書き方

単体テストやWidgetテストとは異なり、インテグレーションテストはアプリとは異なるプロセスで実行されます。そのため、 test_driver ディレクトリを作成し、配下に app.dartapp_test.dart を作成します。
ファイル構成は以下のようになります。

app.dartの実装

app.dart は、FlutterDriverを有効化し、アプリをドライブする実装をします。

app_test.dartの実装

app_test.dart には、テストスイートを実装します。

GreetingPageのテストと、カウンター画面(MyHomePage)への
テストコードの中身を見ていきましょう。

Widgetの取得方法

Widgetは以下のように取得します。

引数で渡す値は Widget build()key: const Key('name text field') と指定した値を渡します。

Widgetの操作方法

findで取得したWidgetを操作する代表的な方法を紹介します。

tap()メソッド

引数に渡されたボタンをタップします。

enterText()メソッド

現在フォーカスが当たっているTextFieldにテキストを入力します。(既存のテキストは削除されます。)
WidgetTestの時とは異なり、あらかじめTextFieldをタップしてフォーカスを当てておく必要があるため、 tap() メソッドとセットで使うケースがほとんどだと思います。

画面表示結果の検証

expect() で検証します。

第1引数で driver.getText() メソッドを呼び出し、Widgetのテキストを取得します。第2引数に渡した文字列と合致しているかどうか、検証されます。

テストの実行方法

実機もしくはエミュレータ を起動している状態で、以下コマンドを実行すると、テストが実行されます。

テストが成功すると、以下のように出力されます。

さいごに

Flutterでは単体からインテグレーションテストまで、幅広くテストを自動化できます。手軽に実装できるので、どんどん実戦投入していきたいものですね。

おすすめ書籍

基礎から学ぶ Flutter Flutter モバイルアプリ開発バイブル プログラミング言語Dart

blog-page_footer_336




blog-page_footer_336




-iOS, Android
-, , ,

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

swift

[Swift4]UITextViewにプレースホルダをつける

1 はじめに1.1 前提条件2 実装3 さいごに はじめに こんにちは、nukkyです。 UITextFieldにはプレースホルダが標準で用意されていますが、UITextViewには用意されていません ...

【Swift】iOS14でブラウザが起動しない。

1 はじめに2 ブラウザの起動について3 原因について4 修正について5 おまけ:メールについて6 さいごに7 おすすめ書籍 はじめに こんにちはsuzukiです。iOS14がリリースされてしばらく経 ...

swift

【Swift】switch文でタプルを使う

1 はじめに2 タプルとは3 タプルを使うswitch文について4 テストコード5 さいごに はじめに こんにちはsuzukiです。本日で投稿が2回目となります。 今後ともよろしくお願い致します。 最 ...

iOS14を控えた今こそUINavigationBarAppearance

1 はじめに2 UINavigationBarAppearance2.1 タイトルのカスタマイズ2.2 背景のカスタマイズ2.3 UIBarButtonItemAppearance2.4 ボタンタイト ...

[Swift]MapViewでクラスタ機能を利用する。

1 はじめに2 クラスタリングとは3 PINのクラスタリング機能を利用する3.1 マップの作成3.2 PINの作成3.3 Pinのカスタマイズ3.4 カスタムしたPinの利用3.5 CustomPin ...

フォロー

blog-page_side_responsive

2020年3月
1234567
891011121314
15161718192021
22232425262728
293031  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。