[Flutter] : TDD - 03.Domain Layer Refactoring
이전 내용에서 작성하였던 Domain 레이어를 리펙토링 해보겠다.
먼저 알아 두면 좋은 Dart언어의 특징 중 하나는
Object 내부 call 함수를 2가지 방식으로 호출 할 수 있다는 것이다.
Object.call();
Object();
call 함수만 가능
이제 다시 저번에 작성한 우리 usecase코드와 test코드를 확인해보자.
class GetConcreteNumberTrivia {
final NumberTriviaRepository repository;
GetConcreteNumberTrivia(this.repository);
Future<Either<Failure, NumberTrivia>> excute({@required int number}) async {
return await repository.getConcreteNumberTrivia(number);
}
}
void main() {
...
test('should get tirivia for the number from the repository', () async {
// arrange
when(mockNumberTriviaRepository.getConcreteNumberTrivia(any))
.thenAnswer((_) async => Right(tNumberTirivia));
// act , 아직 구현되지 않음 함수
final result = await usecase.excute(number: tNumber);
// assert
expect(result, Right(tNumberTirivia));
// Repository에서 함수가 호출되었는지 확인
verify(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber));
// 위의 방법만 호출하고 더 이상 호출하면 안된다.
verifyNoMoreInteractions(mockNumberTriviaRepository);
});
}
여기서 usecase.excute를 usecase.call로 즉 usecase()로 호출 할 수 있다.
그리고 모든 usecase는 call 함수가 필요하다. 즉 인터페이스로 만들자.
수정 해보자.
// core/usecases/usecase.dart
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:number_trivia/core/error/failures.dart';
abstract class UseCase<Type, Params> {
Future<Either<Failure, Type>> call(Params params);
}
class NoParams extends Equatable {}
공통 인터페이스를 만들고 이제 이걸로 수정해보자.
domain/usecases/get_concrete_number_trivia.dart
class GetConcreteNumberTrivia extends UseCase<NumberTrivia, Params> {
final NumberTriviaRepository repository;
GetConcreteNumberTrivia(this.repository);
Future<Either<Failure, NumberTrivia>> call(Params params) async {
return await repository.getConcreteNumberTrivia(params.number);
}
}
class Params extends Equatable {
final int number;
Params({@required this.number});
@override
List<Object> get props => [number];
}
equatable 최신 버전에서는 생성자에 값을 넣어주는 것이 아니라 get props 함수를 구현해주어야 비교 연산이 동작한다. 이전 코드들도 수정하자.
test/domain/usecases/get_concrete_number_trivia_test.dart
...
final result = await usecase(Params(number: tNumber));
...
정상적으로 테스트가 돌아간다!
이제 get_random_number_trivia_test를 구현해보자.
TDD 개발론 처럼 먼저 test코드를 작성하자.
class MockNumberTriviaRepository extends Mock
implements NumberTriviaRepository {}
void main() {
GetRandomNumberTrivia usecase;
MockNumberTriviaRepository mockNumberTriviaRepository;
setUp(() {
mockNumberTriviaRepository = MockNumberTriviaRepository();
usecase = GetRandomNumberTrivia(mockNumberTriviaRepository);
});
final tNumberTrivia = NumberTrivia(text: 'test', number: 1);
test('should get trivia from the repository', () async {
//arrange
when(mockNumberTriviaRepository.getRandomNumberTrivia())
.thenAnswer((_) async => Right(tNumberTrivia));
//act
// 랜덤 숫자는 파라미터가 필요없다 그냥 넘겨주자
final result = await usecase(NoParams());
//assert
expect(result, Right(tNumberTrivia));
verify(mockNumberTriviaRepository.getRandomNumberTrivia());
verifyNoMoreInteractions(mockNumberTriviaRepository);
});
}
이제 usecase를 구현해보자.
class GetRandomNumberTrivia extends UseCase<NumberTrivia, NoParams> {
NumberTriviaRepository repository;
GetRandomNumberTrivia(this.repository);
@override
Future<Either<Failure, NumberTrivia>> call(NoParams params) async {
return await repository.getRandomNumberTrivia();
}
}
이제 테스트 코드를 동작시키면 정상적으로 잘 돌아간다.