1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-12-20

前回までの流れ

  • 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にはならないし、テストに抜け漏れが発生する

関数型エイリアス

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

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
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?