search
LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

Flutter TDD Clean Architecture Course その6 - Repositoryの実装

前回までの流れ

  • CleanArchitectureの図の内側から外側に向かって以下のようにコーディングをしています
    • Entityの作成
    • UseCaseの作成
    • RepositoryのContractの作成
    • Modelの作成
    • DataSourcesのContractの作成
    • Repositoryの実装ファイルの作成(途中まで)

今回やること

  • Repositoryの実装をするために、1つ外側のDataSourcesのContractを作成してあります。
    • 今回はこのDataSourcesのContractのMockを用いてRepositoryの実装をします

getConcreteNumberTrivia

NetworkInfoを呼ぶというテスト実装

number_trivia_repository_impl_test.dart
group('getConcreteNumberTrivia', () {
  // DATA FOR THE MOCKS AND ASSERTIONS
  // We'll use these three variables throughout all the tests
  final tNumber = 1;
  final tNumberTriviaModel =
      NumberTriviaModel(number: tNumber, text: 'test trivia');
  final NumberTrivia tNumberTrivia = tNumberTriviaModel;

  test('should check if the device is online', () {
    //arrange
    when(mockNetworkInfo.isConnected).thenAnswer((_) async => true);
    // act
    repository.getConcreteNumberTrivia(tNumber);
    // assert
    verify(mockNetworkInfo.isConnected);
  });
});
number_trivia_repository_impl.dart
@override
Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(int number) {
  networkInfo.isConnected;
  return null;
}
  • この実装は何もしていませんが、NetworkInfoが呼ばれるというテストを通している
    • テストを通すコードのみを追加するというのがTDDの手法になる

主要なテスト

例外発生時のテスト

test.dart
test(
  'should return server failure when the call to remote data source is unsuccessful',
  () async {
    // arrange
    when(mockRemoteDataSource.getConcreteNumberTrivia(tNumber))
        .thenThrow(ServerException());
    // act
    final result = await repository.getConcreteNumberTrivia(tNumber);
    // assert
    verify(mockRemoteDataSource.getConcreteNumberTrivia(tNumber));
    verifyZeroInteractions(mockLocalDataSource);
    expect(result, equals(Left(ServerFailure())));
  },
);
impl.dart
@override
Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(
  int number,
) async {
  networkInfo.isConnected;
  try {
    final remoteTrivia =
        await remoteDataSource.getConcreteNumberTrivia(number);
    localDataSource.cacheNumberTrivia(remoteTrivia);
    return Right(remoteTrivia);
  } on ServerException {
    return Left(ServerFailure());
  }
}
  • DataSourceで例外を投げた場合に、RepositoryはcatchしてFailureというエラーを表すインスタンスを返す
    • Eitherを用いる

getRandomNumberTrivia

2つのユースケースでのリファクタリング

  • getConcreteNumberTriviagetRandomNumberTriviaを共通化する
    • 以下のような関数型エイリアアスを用いて、remoteDataSource.getConcreteNumberTrivia(number)remoteDataSource.getRandomNumberTrivia()を引数でとれるようにする
typedef Future<NumberTrivia> _ConcreteOrRandomChooser();
impl.dart
typedef Future<NumberTrivia> _ConcreteOrRandomChooser();

class NumberTriviaRepositoryImpl implements
...
@override
  Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(
    int number,
  ) async {
    return await _getTrivia(() {
      return remoteDataSource.getConcreteNumberTrivia(number);
    });
  }

  @override
  Future<Either<Failure, NumberTrivia>> getRandomNumberTrivia() async {
    return await _getTrivia(() {
      return remoteDataSource.getRandomNumberTrivia();
    });
  }

  Future<Either<Failure, NumberTrivia>> _getTrivia(
    _ConcreteOrRandomChooser getConcreteOrRandom,
  ) async {
    if (await networkInfo.isConnected) {
      try {
        final remoteTrivia = await getConcreteOrRandom();
        localDataSource.cacheNumberTrivia(remoteTrivia);
        return Right(remoteTrivia);
      } on ServerException {
        return Left(ServerFailure());
      }
    } else {
      try {
        final localTrivia = await localDataSource.getLastNumberTrivia();
        return Right(localTrivia);
      } on CacheException {
        return Left(CacheFailure());
      }
    }
  }
}

学んだこと

DataSourceは例外を投げるように設計する

  • 前回まで強調されていたように、アプリケーションの外側との境界であるDataSourceは例外を投げる
    • 外部の以上は明示的に例外として投げる
      • 404 error
      • Cacheが存在しない
  • Repositoryは例外をcatchしてエラーを返す
    • このチュートリアルではEither<Failure, NumberTrivia>を用いる

TDDでの実装はテストを通すことのみに集中する

  • NetworkInfoが呼ばれているというテストを通す実装では、NetworkInfoを呼ぶというコードだけ書く。他のコードは書かない。
    • 他のコードはその部分のテストを通すために書く
      • テストが失敗していない状態で実装を書き足すと、TDDにはならないし、テストに抜け漏れが発生する

関数型エイリアス

  • いずれ改めて記事を書きます。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2