flutterでのインテグレーションテスト＆Riverpodを使用したテストコードへのprovider情報の引き出し。

# はじめに

この記事は、私、大阪国際工科専門職大学 [⧉](https://www.iput.ac.jp/osaka/)、 情報工学科、２年のuroketa(本名は伏せます)が、大学のカリキュラムで、iPresence合同会社 [⧉](https://ipresence.jp/) の企業内で実習に臨まさせて頂いた時に作成したものです。
今回は、iPresence合同会社が開発しているアプリケーションのインテグレーションテストに触れさせて頂いた時の経験を記事にまとめてさせて頂きました。

## ☆インテグレーションテスト
今回、私が触れさせて頂いたインテグレーションテストは「統合テスト」とも呼ばれており、複数のプログラムやモジュールを同時に稼働して、実稼働時に近い環境で行うテストです。
今回は、**VScode**を使ってコードに触れていきました。

### ➀flutterでのインテグレーションテストのコード
まずは、下の例コードを見てください。
**注意！** ( )の中の説明は//で囲まれています。' ' や , や ( ) はコードの一部なので気をつけてください。
#### app_test.dart(例コード)

    //コードを記述するのに必要なパッケージをインポート
    import 'package:○○○/○○○.dart'
    etc...
    
    //処理開始
    void main(){

     //大元のグループ
     group('//ここにグループ名を記述。例→robot test//', () {　　　　　　　

      //テストの種類によって場合分けをしてまとめる。
      testWidgets('//ここにテスト名を書く。例→No.1 test login test//',
       (//テストを通して呼び出したい関数があればここに記述//) {        

       //ここに呼び出したい関数やテストしたい処理を記述
       //例→app.main();  //main関数呼び出し
       //例→printconsole(○○○○○○,);  記述した文字列はデバックコンソールにも表示されるので便利。
       //例→expect(//変数//,//左の変数の期待値//);
       //expectは変数の値が期待値だと成功のメッセージを送ってくれる。
       //処理を記述した後にexpectでチェックするようなテストの形を作ることが出来る。

      }
     });
    }
私が触れたインテグレーションテストのコードを**ざっくりと**表すとこのような感じでした。
大元のgroupの中に複数のテストを記述していく形でインテグレーションテストのコードが形作られていました。

### ➁インテグレーションテストコードのrun
そして、インテグレーションテストのコードをrunさせてテストを行うのですが、下のような形でコードの中に記述されたexpectの結果がVScodeのデバックコンソール画面に表示されます。
**注意！** 下の黒画面の○○○○○○は同じ文字が入る、という意味ではありません！Qiita公開にあたって情報を隠すために○○○○○○としています。

    //ここにprintconsoleで記述した文字列がアウトプットされる。
    //printconsoleを使うとデバックコンソールが見やすくなる。
    [38;5;12m┌───────────────────
    [38;5;12m│ #0   ○○○○○○ (package:○○○○○○.dart:82:12)
    [38;5;12m│ #1   FormFieldState.save (package:flutter/src/widgets/form.dar:419:21)
    [38;5;12m│ #2   FormState.save (package:flutter/src/widgets/form.dart:210:13)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 17:49:08.747 (+0:00:46.971183)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 💡 54545
    [38;5;12m└───────────────────
    [38;5;12m┌───────────────────
    [38;5;12m│ #0   ○○○○○○ (package:○○○○○○.dar:214:12)
    [38;5;12m│ #1   ○○○○○○ (package:○○○○○○.dart:107:14)
    [38;5;12m│ #2   ○○○○○○.<anonymous closure> (package:○○○○○○.dart:299:36)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 17:49:08.758 (+0:00:46.982618)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 💡 ---inside cloud func call
    [38;5;12m└───────────────────
    ┌───────────────────
    │ #0   ○○○○○○.<anonymous closure> (package:○○○○○○.dart:20:10)
    │ #1   ○○○○○○.<anonymous closure> (package:○○○○○○.dart:75:43)
    │ #2   ○○○○○○ (package:○○○○○○.dart:89:27)
    ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    │ 17:49:08.782 (+0:00:47.006478)
    ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    │ 🐛 ○○○○○○ //ここに変数の情報がアウトプットされる。
    └───────────────────
    ┌───────────────────
    │ #0   ○○○○○○.<anonymous closure>.<anonymous closure> (package:○○○○○○.dart:25:22)
    │ #1   ○○○○○○.<anonymous closure> (package:○○○○○○.dart:241:38)
    │ #2   ○○○○○○ (package:○○○○○○.dart:161:16)
    ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    │ 17:49:08.786 (+0:00:47.009630)
    ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    │ 🐛 ○○○○○○ //ここに変数の情報がアウトプットされる(上とはまた別)
    └───────────────────
    [38;5;12m┌───────────────────
    [38;5;12m│ #0   DynamicLinkStateNotifier.log (package:○○○○○○.dart:14:12)
    [38;5;12m│ #1   ○○○○○○.<anonymous closure> (package:○○○○○○.dart:138:8)
    [38;5;12m│ #2   ○○○○○○.<anonymous closure> (package:○○○○○○.dart:57:43)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 17:49:08.795 (+0:00:47.018930)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 💡 null
    [38;5;12m└───────────────────
    [38;5;12m┌───────────────────
    [38;5;12m│ #0   ○○○○○○ (package:○○○○○○.dart:108:16)
    [38;5;12m│ #1   <asynchronous suspension>
    [38;5;12m│ #2   ○○○○○○.<anonymous closure> (package:○○○○○○.dart:124:15)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 17:49:09.111 (+0:00:47.335022)
    [38;5;12m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    [38;5;12m│ 💡 {
    [38;5;12m│ 💡   "error_code": 1,
    [38;5;12m│ 💡   "message": "○○○○○○",
    [38;5;12m│ 💡   "isSuccess": false
    [38;5;12m│ 💡 }
    [38;5;12m└───────────────────

    🔄//次のprintconsoleがここにアウトプットされる。
    [38;5;12m┌───────────────────(下に続く)


## ☆他dartファイルからの情報の引き出し
ここからは、インテグレーションテストで触れたアプリケーションのコード内で触れた、他dartファイルからの情報の引き出しについて書いていこうと思います。

１つのアプリを形作るためには、例えば画面の情報が記述されたdartファイルや、ログインする必要があるアプリならログイン処理やe-mailリンクの処理が記述されたdartファイルなど沢山のdartファイルが必要になります。インテグレーションテストはそれらのdartファイルから様々な情報を **関数やkey** としてapp_test.dartに引っ張ってきて、テストします。

↓↓↓ keyとして変数にapp_test.dartに情報を引っ張ってきてテストに組み込む例 ↓↓↓

    final 変数名 = find.byKey(const Key('//keyとして設定した文字列 例→12345//'));
例えばapp_test.dartに上のように書くと、12345キーが設定されている別dartファイルの処理を呼び出すことが出来ます。また、

    expect(find.byKey(const Key('//keyとして設定した文字列//')), //期待値//);
という感じでexpectに呼び込むこともできます。

また、一番初めの題名にもあるように、app.test_dartに実行中のRiverpodを使用したアプリからProvider情報を引き出す方法として、下のコードのような方法を使いました。

    //keyに当てはまるwidgetを検索して、そのwidgetが入ったbuildcontextをゲットする。
    //testerは１つのtestの括り毎に定義しているtestを通して呼び出している変数。
    final BuildContext context =
      tester.element(find.byKey(const Key('telepiiLoginText')));

    // get  providerContainer to override
    //ゲットしたbuildcontextを使って、ProviderScopeの中にあるコンテナをゲットする。
    final container = ProviderScope.containerOf(context);

    //ゲットしたコンテナを使って、initialDynamicLinkFutureProviderの値を読み取る
    final dynamicLink = container.read(initialDynamicLinkFutureProvider).value;

## まとめ
ここまで、flutterでのインテグレーションテストの簡単な説明、他dartファイルから情報を引き出す方法についてまとめました。また私は、インテグレーションテストのコードの中に、自動でログイン認証を突破させる処理を追加することにもトライしていました。完成させることはできなかったのですが、何度もトライ＆エラーを重ねたこの経験は、とても今後のタメになるモノだったなと感じています。