はじめに
Flutterでユニットテストを行う際、Mockitoを使用してモックオブジェクトを作成することが一般的です。しかし、特定の状況下(今回はsealed
クラスをモック化した)ではエラーが発生し、テストの実行が妨げられることがあります。本記事では、provideDummy
とprovideDummyBuilder
を使用してそのようなエラーを解決する方法を解説します。
エラーの発生するコード
以下のコードを実行するとエラーが発生します。
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();
});
メソッドの挙動の確認
以下のテストコードで、provideDummy
とprovideDummyBuilder
の挙動を確認します。
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
型が必要なときに呼び出され、ダミー値を動的に生成します。- ビルダー関数内で、
parent
やinvocation
を使用して、呼び出し元のコンテキストや状況に応じたダミー値を返すことができます。
まとめ
MissingDummyValueError
は、Mockitoが特定の型のダミー値を自動生成できないときに発生します。これは主にsealed
クラスや抽象クラスなどで起こります。この問題を解決するためには、provideDummy
またはprovideDummyBuilder
を使用して、Mockitoにダミー値を提供する必要があります。
provideDummy
は、特定のインスタンスをダミー値として提供します。provideDummyBuilder
は、ダミー値を生成する関数を提供し、動的なダミー値の生成が可能です。
これらの機能を適切に活用することで、Mockitoでのテストがより円滑に進み、テストコードの柔軟性と信頼性が向上します。