🥰

Supabaseでテストコード書けませんか?

2024/07/10に公開

試しにやってみた

fake_cloud_firestoreみたいに、モックのパッケージが内容だ....
ないなら、テスト用のテーブル作って入れていいのではと思った?

とりあえず作ってみる。関数を作ってみよう。test_apiフォルダを作成して、テストコード用のINSERTできるクラスを作成。実行すると本当に保存しちゃいます😅

test_api
import 'package:supabase_flutter/supabase_flutter.dart';

final class TodoAPi {
  final supabase = Supabase.instance.client;

  Future<void> insertTodo({required String title}) async {
    // 日本時間のtimestampzを作成
    final now = DateTime.now().toUtc().add(const Duration(hours: 9));
    final response = await supabase.from('dev_todo').insert({
      'title': title,
      'created_at': now.toIso8601String(),
    });
  }
}

todo_api_test.darttest/ディレクトリ配下に作成する。

テストコードを書くところでは、.envから読み込めなかった😅
URLとanonKyeを直接ハードコーディングしている。

idは自動連番なので、自動生成されるから引数で渡す必要はなし。タイムスタンプもメソッドの中に書いてるので、引数で渡す必要はなし。引数には、タイトルだけ渡します。

import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:workstream_prod/application/test_api/todo_api.dart';

void main() {
  setUpAll(() async {
    // Set initial values for shared preferences
    SharedPreferences.setMockInitialValues({});

    const url = 'https:/**************';
    const anonKey =
        'eyJhbGciOiJIU**********';
    await Supabase.initialize(url: url, anonKey: anonKey);
  });

  test('TodoAPIのテスト', () async {
    final todoApi = TodoAPi();
    await todoApi.insertTodo(title: '宿題をする');
  });
}

テストを実行する。

やって良いのか。実際に保存されているので、成功していることになる。

でもやはりモックがいいな

Supabase関係ないコードで作るくしかないですね。同じデータ型の値を渡して、期待通りの値が渡されているモックを作るしかなさそう。mockitoを使ってみた。

https://pub.dev/packages/mockito
https://pub.dev/packages/build_runner

新しく専用のファイルを作成

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';

import 'mock_todo_test.mocks.dart';

abstract class Todo {
  Map<String, dynamic> insertTodo({required String title});
}

([Todo])
void main() {
  test('MockTodo insertTodo test', () {
    // Arrange
    var mockTodo = MockTodo();

    when(mockTodo.insertTodo(title: anyNamed('title'))).thenAnswer((_) {
      return {
        'id': 1,
        'title': 'Test Title',
        'created_at': DateTime.now().toIso8601String(),
      };
    });

    /// [Act]
    /// Actとは、テスト対象のメソッドを呼び出すことです。
    var result = mockTodo.insertTodo(title: 'Test Title');

    /// [Assert]
    /// Assertとは、テスト結果を検証することです。
    expect(result['id'], 1);
    expect(result['title'], 'Test Title');

    // Verify the method call.
    verify(mockTodo.insertTodo(title: 'Test Title')).called(1);
  });
}

自動生成のコマンドを実行する:

flutter pub run build_runner watch --delete-conflicting-outputs

実行結果:

一応成功しましたね😅

まとめ

Supabaseの単体テスト(UnitTest)かな...
書く方法はまだないのかもしれない。そもそもテストとはなんのためにするのか?

単純に書いた関数が、シナリオ通りに実行されているか確かめるためでしょうけどね。

最近読んだ本によると

単体テストの定義には様々なものがあります。本質的ではないものを除くと、 単体テスト として定義されるテストには次に挙げる 3 つの重要な性質がすべて備えられていること になります。つまり、自動化されていて、次の 3 つの性質をすべて備えるものが単体テストとな るのです :

  1. 「単体(unit)」と呼ばれる少量のコードを検証する
  2. 実行時間が短い
  3. 隔離された状態で実行される

参考にした記事

Discussion