🔄

TDDのサイクルが自動的にポモドーロになるアプリを作った

2024/12/17に公開

この記事はjig.jp Advent Calender 2024、17日目の記事です。

こんにちは、ダイスです。この記事では、作成したFlutterアプリと開発サイクルについて紹介します。

用語説明

まずはTDDとポモドーロテクニックについて簡単に説明します。

TDDとは

TDDは、テスト駆動開発 (Test Driven Development)のことです。
テストリストからテストを作成→テストを成功→リファクタリングの流れをベースに作業する、プログラミングのワークフローです。
詳しくは以下の記事を参考ください。

https://t-wada.hatenablog.jp/entry/canon-tdd-by-kent-beck

ポモドーロテクニックとは

ポモドーロテクニックは、時間を区切って作業する方法です。
25分の作業時間と、5分の休憩時間を繰り返して作業を進めます。
詳しくは以下の記事を参考ください。

https://asana.com/ja/resources/pomodoro-technique

作ったアプリ

TDDサイクルでポモドーロテクニックができるアプリを作りました。

https://github.com/dicenull/pomodoro_headgear

app-preview

アプリには、ポモドーロタイマーとToDoリストがあります。
TODOをDOING状態にすると、ポモドーロタイマーが自動的に開始します。
ポモドーロタイマーを停止すると、DOING状態からTODOに戻ります。

制作のきっかけ

社内には開発共有というグループをまたいだ共有の場があります。
そこで開催された、テスト駆動開発ハンズオンがこのアプリを作るきっかけです。

TDDはテストすることをテストリストで管理するのですが、このテストリストをどこに書くと良いのかという疑問が生まれました。ここからテストリストを管理するアプリがあるといいのでは?と思い、制作に至りました。

制作の流れ

このアプリもTDDサイクルを使って制作しました。
テストリストは、機能ごとにディレクトリを作り、MarkdownファイルをToDoリストとして運用しました。

features
├── headgear
│   └── headgear_testlist.md
├── pomodoro
│   └── pomodoro_testlist.md
└── todo
    └── todo_testlist.md

ToDoリストのタイトルをそのままテストのタイトルにして、あとはTDDの流れで実装することができました。
詳しい制作の流れを、ポモドーロタイマー機能を例に説明します。

ポモドーロタイマー制作の流れ

まずはポモドーロタイマーの機能を満たすためのテストを列挙しました。

- [ ] ポモドーロを開始すると、作業中状態になる
- [ ] 作業中から25分後に、ポモドーロが完了する
- [ ] ポモドーロ完了から5分後に、ポモドーロが作業中になる

次にテストの実装です。1つピックアップして、テストを実装しました。

void main() {
  (ProviderSubscription<PomodoroState>, PomodoroController) buildSut() {
    final container = createContainer();
    final subsc = container.listen(pomodoroControllerProvider, (_, __) {});
    final controller = container.read(pomodoroControllerProvider.notifier);

    return (subsc, controller);
  }

  test('ポモドーロを開始すると、作業中状態になる', () {
    final (subsc, controller) = buildSut();

    controller.start();

    expect(
      subsc.read().status,
      PomodoroStatus.work,
    );
  });
}

ここでbuildSutメソッドは、テスト対象システム (System Under Test)を作るテスト用のヘルパーメソッドです。このテストではPomodoroControllerがテスト対象システムになります。
テストを実行し、失敗することを確認します。

> flutter test
00:01 +0: ポモドーロを開始すると、作業中状態になる
00:01 +0 -1: ポモドーロを開始すると、作業中状態になる [E]                                            
  Expected: PomodoroStatus:<PomodoroStatus.work>
    Actual: PomodoroStatus:<PomodoroStatus.rest>
...

テストが実装できたので、テストを成功させます。コントローラに状態を変更するメソッドを追加しました。


class PomodoroController extends _$PomodoroController {
  
  PomodoroState build() => PomodoroState();

  void start() {
    state = state.copyWith(
      status: PomodoroStatus.work,
    );
  }
}

テストを実行して、成功していますね!

> flutter test 
00:00 +0: ポモドーロを開始すると、作業中状態になる
00:00 +1: ポモドーロを開始すると、作業中状態になる
00:00 +1: All tests passed!

あとはテストリストがなくなるまで、この流れを繰り返します。
また、必要に応じてリファクタリングしたり、途中で思いついたテストをリストに追加したりします。
以降の流れをそのままコミットしたので参考ください。

https://github.com/dicenull/pomodoro_headgear/pull/1/commits

感想

開発中にTDDワークフローとポモドーロと連動させたい!と、欲しい機能を思い付いたのが楽しかったです。

UnitTestはTDDで扱いやすく、反対にWidgetTestは難しい印象でした。特にVRT (Visual Regression Test)は、Widgetが出来てからでないと作れないため難しく感じました。
WidgetTestについては、Flutterのホットリロードを生かして、Widgetを作り上げてからテストを書く流れに落ち着きました。
まとめると、TDDサイクルにUnitTestを使い、1つのWidgetが出来たらVRTを書くのが良いと感じました。

jig.jp Engineers' Blog

Discussion