FlutterのMockitoでのprovideDummyとprovideDummyBuilderの使い方

  • 2024年10月3日
  • 2024年10月3日
  • test

はじめに

Flutterでユニットテストを行う際、Mockitoを使用してモックオブジェクトを作成することが一般的です。しかし、特定の状況下(今回はsealedクラスをモック化した)ではエラーが発生し、テストの実行が妨げられることがあります。本記事では、provideDummyprovideDummyBuilderを使用してそのようなエラーを解決する方法を解説します。

エラーの発生するコード

以下のコードを実行するとエラーが発生します。

final target = MockCounterPresenter();
when(target.counter).thenReturn('255');

final mockFormatter = MockThousandsSeparatedFormatter();
when(target.selectedFormatter).thenReturn(mockFormatter);

エラー内容

The following MissingDummyValueError object was thrown running a test:
  MissingDummyValueError: NumberFormatter

This means Mockito was not smart enough to generate a dummy value of type
'NumberFormatter'. Please consider using either 'provideDummy' or 'provideDummyBuilder'
functions to give Mockito a proper dummy value.

Please note that due to implementation details Mockito sometimes needs users
to provide dummy values for some types, even if they plan to explicitly stub
all the called methods.

エラーの原因

エラーの原因は、NumberFormatterクラスがsealedであるため、Mockitoが自動的にダミーインスタンスを生成できないことにあります。

sealed class NumberFormatter {
  String get name;
  String format(int value);
}

sealedクラスは継承やインスタンス化に制限があるため、MockitoはNumberFormatter型のダミー値を生成できず、MissingDummyValueErrorが発生します。
また、NumberFormatter自体はモックかできなかったので、その継承クラスThousandsSeparatedFormatterをモック化しました。

解決方法

この問題を解決するために、MockitoはprovideDummyまたはprovideDummyBuilderを提供しています。これらの関数を使用して、Mockitoに手動でダミー値を提供することができます。

provideDummyの使用

provideDummy<T>(T dummy)を使用して、特定の型Tのダミーインスタンスを提供します。

final target = MockCounterPresenter();
when(target.counter).thenReturn('255');

final mockFormatter = MockThousandsSeparatedFormatter();
provideDummy<NumberFormatter>(mockFormatter);

これにより、NumberFormatter型が必要なときにmockFormatterが使用され、エラーが解消されます。

provideDummyBuilderの使用

provideDummyBuilder<T>(T Function(Object? parent, Invocation invocation) builder)を使用して、ダミーインスタンスを動的に生成する関数を提供します。

provideDummyBuilder<NumberFormatter>((parent, invocation) {
  // 必要に応じてダミーインスタンスを生成
  return MockThousandsSeparatedFormatter();
});

メソッドの挙動の確認

以下のテストコードで、provideDummyprovideDummyBuilderの挙動を確認します。

group('test of mockito', () {
  test('provideDummy', () {
    final mockFormatter1 = MockThousandsSeparatedFormatter();
    final mockFormatter2 = MockThousandsSeparatedFormatter();

    final target = MockCounterPresenter();

    provideDummy<NumberFormatter>(mockFormatter1);
    expect(target.selectedFormatter, mockFormatter1);
    expect(target.selectedFormatter, isNot(mockFormatter2));

    provideDummy<NumberFormatter>(mockFormatter2);
    expect(target.selectedFormatter, mockFormatter2);
    expect(target.selectedFormatter, isNot(mockFormatter1));
  });

  test('provideDummyBuilder', () {
    final mockFormatter1 = MockThousandsSeparatedFormatter();
    final mockFormatter2 = MockThousandsSeparatedFormatter();

    final target = MockCounterPresenter();

    provideDummyBuilder<NumberFormatter>((parent, invocation) {
      expect(parent, target);
      print(invocation);
      print(invocation.memberName); // Symbol("selectedFormatter")
      print(invocation.isGetter); // true
      return mockFormatter1;
    });
    expect(target.selectedFormatter, mockFormatter1);
    expect(target.selectedFormatter, isNot(mockFormatter2));

    provideDummyBuilder<NumberFormatter>(
        (parent, invocation) => mockFormatter2);
    expect(target.selectedFormatter, mockFormatter2);
    expect(target.selectedFormatter, isNot(mockFormatter1));
  });
});

テスト結果の解説

  • provideDummyの場合:

    • provideDummyに渡したインスタンスが、NumberFormatter型のダミー値として使用されます。
    • 再度provideDummyを呼び出すと、ダミー値が新しいインスタンスに更新されます。
  • provideDummyBuilderの場合:

    • provideDummyBuilderに渡した関数が、NumberFormatter型が必要なときに呼び出され、ダミー値を動的に生成します。
    • ビルダー関数内で、parentinvocationを使用して、呼び出し元のコンテキストや状況に応じたダミー値を返すことができます。

まとめ

MissingDummyValueErrorは、Mockitoが特定の型のダミー値を自動生成できないときに発生します。これは主にsealedクラスや抽象クラスなどで起こります。この問題を解決するためには、provideDummyまたはprovideDummyBuilderを使用して、Mockitoにダミー値を提供する必要があります。

  • provideDummyは、特定のインスタンスをダミー値として提供します。
  • provideDummyBuilderは、ダミー値を生成する関数を提供し、動的なダミー値の生成が可能です。

これらの機能を適切に活用することで、Mockitoでのテストがより円滑に進み、テストコードの柔軟性と信頼性が向上します。

参考