AIを活用したコーディング支援ツールのGitHub Copilotを使用して、ユニットテストを作ってみようと思います。

GitHub Copilotとは

GitHub Copilotとは、AIが条件にあった最適なコードを提案し、プログラマーのコーディング作業を支援するサービスです。対応言語も多岐にわたり、今回紹介するFlutter以外にもPython、JavaScript、Javaなど、主要なプログラミング言語をサポートしています。

ユニットテストとは

ユニットテストとは、ソフトウェア開発において、個々の機能や部品(ユニット)が正常に動作するかどうかを検証するテストのことです。実装と同時にユニットテストを実施することで、コードの品質を高め、バグの早期発見や修正に繋げることができます。

使用するアプリ

公式ドキュメントにあるこちらのプロジェクトを使用しました。

https://dartpad.dev/?id=e7076b40fb17a0fa899f9f7a154a02e8

Nextボタンを押下するとランダムな単語が表示され、Likeを押下するといいねマークが単語の隣に表示されます。フッターのFavoriteを押下すると、Likeした単語が表示されるというアプリです。


ユニットテスト作成

VSCodeにGitHub Copilotの拡張機能はインストール済みの前提とします。

1、テストを作成したいファイルを表示させた状態でチャットを表示し、/testsと入力します。

2、以上の作業だけで、こちらのテストコードを作成してくれました。

// filepath: /Users/sakashita/Desktop/flutter_application_1/lib/main_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:english_words/english_words.dart';
import 'main.dart';

void main() {
  group('MyAppState Tests', () {
    test('getNext updates current and history', () {
      final state = MyAppState();
      final initialCurrent = state.current;
      state.getNext();
      expect(state.current, isNot(initialCurrent));
      expect(state.history.first, initialCurrent);
    });

    test('toggleFavorite adds/removes favorites', () {
      final state = MyAppState();
      final pair = WordPair.random();
      state.toggleFavorite(pair);
      expect(state.favorites.contains(pair), true);
      state.toggleFavorite(pair);
      expect(state.favorites.contains(pair), false);
    });

    test('removeFavorite removes from favorites', () {
      final state = MyAppState();
      final pair = WordPair.random();
      state.toggleFavorite(pair);
      expect(state.favorites.contains(pair), true);
      state.removeFavorite(pair);
      expect(state.favorites.contains(pair), false);
    });
  });

  group('Widget Tests', () {
    testWidgets('MyHomePage has BottomNavigationBar', (WidgetTester tester) async {
      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (context) => MyAppState(),
          child: MaterialApp(home: MyHomePage()),
        ),
      );

      expect(find.byType(BottomNavigationBar), findsOneWidget);
    });

    testWidgets('GeneratorPage displays current WordPair', (WidgetTester tester) async {
      final state = MyAppState();
      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (context) => state,
          child: MaterialApp(home: GeneratorPage()),
        ),
      );

      expect(find.text(state.current.asLowerCase), findsOneWidget);
    });

    testWidgets('FavoritesPage displays favorites', (WidgetTester tester) async {
      final state = MyAppState();
      final pair = WordPair.random();
      state.toggleFavorite(pair);

      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (context) => state,
          child: MaterialApp(home: FavoritesPage()),
        ),
      );

      expect(find.text(pair.asLowerCase), findsOneWidget);
    });

    testWidgets('FavoritesPage shows "No favorites yet." when empty', (WidgetTester tester) async {
      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (context) => MyAppState(),
          child: MaterialApp(home: FavoritesPage()),
        ),
      );

      expect(find.text('No favorites yet.'), findsOneWidget);
    });
  });
}

3、一部参照先が間違っていたので修正します。
import 'main.dart';

import 'package:flutter_application_1/main.dart';
に修正しました。

4、状態管理周りのテストを実行するとテストは通るようです。

5、一方Widgetテストは失敗するようです。しかし少し修正すれば使用可能なテストコードになりそうです。

最後に

GitHub Copilotを活用することで、ユニットテストの作成を大幅に効率化できることがわかりました。

そのままのコードでは今回テストは通りませんでしたが、一部修正することでテストコードの自動生成や、テストケースの提案など、テスト作成にかかる時間を短縮し、より重要な開発業務に集中できることが想定されます。

また、普段あまり記述しないようなテストケースなども提案してくれるため、テストの網羅率が上がると思いました。

ユニットテストに当てる時間を効率化し実装に集中できることで、より品質の高いサービスを提供していきたいと思います。

参考文献

https://codelabs.developers.google.com/codelabs/flutter-codelab-first?hl=ja#0

https://docs.github.com/ja/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot