⛰️

Flutterの自動テスト Step1 ~Unit Test(依存なし)~

2023/11/06に公開

Flutter 自動テスト シリーズについて

Flutterの自動テストをStep by Stepで説明しています。

一連の記事を「本」(無料)にまとめました。
https://zenn.dev/yu1ro/books/2b5f8d32cc2668

今回の対象

今回は一番カンタンなテストです。
千里の道も一歩から。
とにかく簡単に始めて、少しでも安全なアプリを目指しましょう。

まずStep1で扱うのは依存の無い関数のテストです。
例えば

  • トップレベル関数
  • 拡張関数
  • フォームのバリデーション

が対象になりそうです。
DateTime.now()を使っている場合は次のStepで取り扱いますので、
使っていないものを対象にしてください。

ディレクトリ構成

ディレクトリは規約等はありませんが、
以下をおすすめしています。

project_root
├── lib
│   ├── utils
│   │   └── foo.dart
│   ├── bar.dart
│   └── main.dart
└── test
    ├── unit
    │   ├── utils
    │   │   └── foo_test.dart
    │   └── bar_test.dart
    ├── widget
    └── integration

テスト対象のコードを用意

lib配下に例えばこんなコードがあるとして、これをテストします。

most_recent.dart
DateTime mostRecent(List<DateTime> dates) {
  return dates.reduce((a, b) => a.isAfter(b) ? a : b);
}

テストコード

まずはファイル作成

test配下にファイルを作成します。
ディレクトリ構成は維持するのがおすすめ。
most_recent_test.dart

mainの中にtest()を書きます

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

void main() {
  test('テストの説明', () {
    // 実際のテストコード
  });
}

仕様をテストコードにする

今回の例では、mostRecentへの入力が2023-01-01と2023-01-02の場合、
2023-01-02が返却されてほしいです。
これをコードで書くとこうなります。

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

void main() {
  test('テストの説明', () {
    final actual = mostRecent([DateTime(2023, 1, 1), DateTime(2023, 1, 2)]);
    expect(actual, DateTime(2023, 1, 2));
  });
}

actualに実際に実行した結果の返却値を入れて、expectの第一引数に渡します。
expectの第二引数には、期待する値を渡します。[1]

実行する

ここまできたら実行しましょう。
Android Studio等ではここをポチッとすれば実行できます

すべてのテストをコマンドラインで実行する場合はこちらのコマンドを実行してください。

flutter test

失敗させる

ここまで手順通りであれば成功するはずですが、
まれにどんなコードでも成功してしまうということがあります。
そんな場合は不具合が混入したときに気がつけなくなってしまうので、テストとしては役にたっていません。
ですので、一度でいいので失敗させましょう。

テスト対象のコードを以下のように書き換えてみましょう。

most_recent.dart
DateTime mostRecent(List<DateTime> dates) {
  return dates.reduce((a, b) => a.isBefore(b) ? a : b);
  // return dates.reduce((a, b) => a.isAfter(b) ? a : b);
}

isBeforeとisAfterを間違えたとすると仕様と異なるので、テストは失敗します。

失敗したことを確認できたら、元に戻して再度成功させましょう。

複数のtestをgroupでまとめたり、説明を記載する

testはgroupでまとめることができます。
また、groupやtestに説明を書きます。この説明がテスト実行時に役に立ちます。

most_recent_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:step_by_step_test/utils/most_recent.dart';

void main() {
  group('Step1-1 mostRecent', () {
    test('複数の日付の場合', () {
      final actual = mostRecent([DateTime(2023, 1, 1), DateTime(2023, 1, 2)]);
      expect(actual, DateTime(2023, 1, 2));
    });

    test('単数の日付の場合', () {
      final actual = mostRecent([DateTime(2023, 1, 1)]);
      expect(actual, DateTime(2023, 1, 1));
    });
  });
}

以上です。

まとめ

これが一番簡単なテストコードになります。
どうでしょう?思っていたより簡単だったのではないでしょうか?
依存が増えたりすると、mock等が出てきますが、基本はここに帰着しますので、
非常に重要なFirst Stepとなったのではないでしょうか?

実行可能なコードはGitHubに公開しています。
https://github.com/yu1ro/step_by_step_test

次の記事はこちら

https://zenn.dev/yu1ro/articles/61ae6f363ff913

最後に(宣伝)

友人と個人開発でゲーミフィケーション記録アプリ「HibaQuest」を作っています。
https://hiba.quest

脚注
  1. 細かいをすると、matcherを渡しますが、このことは別の記事で記載します。 ↩︎

Discussion