- こちらのチュートリアルの解説をします。
- コードは該当ページのGitHubリンクから取得するのが良いと思います。
- チュートリアルを読むのが早いと思いますが、概要をこちらにまとめたのでこちらもご参照ください。
call()メソッド
- call()を定義したクラスについて、
object.call()
をobject()
とも書ける- UseCaseを使う際に便利
get_concrete_number_trivia_test.dart
// before
// final result = await usecase.execute(number: tNumber);
// after
final result = await usecase(number: tNumber);
2つめのUseCaseGetRandomNumberTrivia
を追加する
UseCaseのBaseClass
- cleanなコーディングのためにはBaseClassを作るとよい
- UseCaseはcallメソッドを作るとわかりやすい
- method名を覚えなくて良いため
- 以下のようなBase Classを用いる
- call()にパラメータを使わない場合には
NoParams
を用いる- getRandomNumberTriviaの場合、パラメータを用いないため
- call()にパラメータを使わない場合には
usecase.dart
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import '../error/failure.dart';
// method定義
// Type, ParamsはGenericsである。成功の場合の型をType、受け取るパラメータの型をParamsとして記述している
abstract class UseCase<Type, Params> {
Future<Either<Failure, Type>> call(Params params);
}
// parameterを用いない場合に使うダミーのparameter
class NoParams extends Equatable {}
BaseClassの継承
-
GetConcreteNumberTrivia
にBaseClassをextendsさせる - int numberを渡せる
Params
クラスを作成する- これはintのまま渡してもよい。複数のパラメータを渡す場合はこのクラスを使うのが良いと思う
get_concrete_number_trivia.dart
class GetConcreteNumberTrivia extends UseCase<NumberTrivia, Params> {
...
}
class Params extends Equatable {
final int number;
Params({@required this.number}) : super([number]);
}
- testをリファクタリングする
- executeを省略する
- Paramsを使用する
- 実装が対応していないため、このテストは失敗する。
get_concrete_number_trivia_test.dart
...
test(
'should get trivia for the number from the repository',
() async {
// arrange
when(mockNumberTriviaRepository.getConcreteNumberTrivia(any))
.thenAnswer((_) async => Right(tNumberTrivia));
// act
final result = await usecase(Params(number: tNumber));
// assert
expect(result, Right(tNumberTrivia));
verify(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber));
verifyNoMoreInteractions(mockNumberTriviaRepository);
},
);
- 実装を修正する
get_concrete_number_trivia.dart
class GetConcreteNumberTrivia extends UseCase<NumberTrivia, Params> {
...
@override
Future<Either<Failure, NumberTrivia>> call(Params params) async {
return await repository.getConcreteNumberTrivia(params.number);
}
}
...
GetRandomNumberTrivia
get_random_number_trivia_test.dart
import 'package:clean_architecture_tdd_prep/core/usecase/usecase.dart';
import 'package:clean_architecture_tdd_prep/features/number_trivia/domain/entities/number_trivia.dart';
import 'package:clean_architecture_tdd_prep/features/number_trivia/domain/repositories/number_trivia_repository.dart';
import 'package:clean_architecture_tdd_prep/features/number_trivia/domain/usecases/get_random_number_trivia.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
class MockNumberTriviaRepository extends Mock
implements NumberTriviaRepository {}
void main() {
GetRandomNumberTrivia usecase;
MockNumberTriviaRepository mockNumberTriviaRepository;
setUp(() {
mockNumberTriviaRepository = MockNumberTriviaRepository();
usecase = GetRandomNumberTrivia(mockNumberTriviaRepository);
});
final tNumberTrivia = NumberTrivia(number: 1, text: 'test');
test(
'should get trivia from the repository',
() async {
// arrange
when(mockNumberTriviaRepository.getRandomNumberTrivia())
.thenAnswer((_) async => Right(tNumberTrivia));
// act
// Since random number doesn't require any parameters, we pass in NoParams.
final result = await usecase(NoParams());
// assert
expect(result, Right(tNumberTrivia));
verify(mockNumberTriviaRepository.getRandomNumberTrivia());
verifyNoMoreInteractions(mockNumberTriviaRepository);
},
);
}
- 実装する
get_random_number_trivia.dart
import 'package:dartz/dartz.dart';
import '../../../../core/error/failure.dart';
import '../../../../core/usecase/usecase.dart';
import '../entities/number_trivia.dart';
import '../repositories/number_trivia_repository.dart';
class GetRandomNumberTrivia extends UseCase<NumberTrivia, NoParams> {
final NumberTriviaRepository repository;
GetRandomNumberTrivia(this.repository);
@override
Future<Either<Failure, NumberTrivia>> call(NoParams params) async {
return await repository.getRandomNumberTrivia();
}
}
学んだこと
- UseCaseが複数ある場合はインターフェイスを作るときれいに設計できる
- call()メソッドを使うことでインスタンスを関数化できるのが便利
-
複数UseCaseで異なる引数をとる場合、Genericsを使う
- 引数がない場合のダミーとして
NoParams
を使うのがよい - NoParamsはインターフェイスのファイルに記述しておけば、UseCaseの実装から呼べる
-
GetConcreteNumberTrivia
用のParams
は同ファイルに記述すれば、同UseCaseを呼ぶファイルで同じく呼べる
- 引数がない場合のダミーとして
-
Testを次の3段階に記述するのは覚えておくと何をするか迷わなくてよい
- arrange: mockの作成、DI等の準備、mockの挙動設定
- act: テスト対象メソッドを呼ぶ
- assert: 検証する