Learning TDD with Clean Architecture for Flutter (Domain Layer) Part II
Tags: flutter, tdd, cleanarchitecture, dart
Published: 2022-08-11 16:49:00


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jryd85jvf62219jolngj.png)

Part **I [here](https://dev.to/codingmountain/learning-tdd-with-clean-architecture-for-flutter-part-i-46nk)**
Part **III [here](https://dev.to/codingmountain/learning-tdd-with-clean-architecture-for-flutter-data-layer-part-iii-1fff)**
Part **IV [here](https://dev.to/codingmountain/learning-tdd-with-clean-architecture-for-flutter-part-iv-16b4)**

Welcome back to the Part II of this series. Let’s continue from where we left. Here we will begin to code and finish TDD for the domain layer.

## TDD Clean Architecture

Now we know a little bit about TDD and Clean Architecture, let us combine those two to get the best of both worlds. Before jumping onwards here are a few things :

-  We will be writing a unit test.
-  We will be using Mocktail to mock the dependencies.

```
 class MockAwsDataSource extends Mock implements AwsDataSource {} 
    // To mock class AwsDataSourece using mocktail
```

-  While testing any class, mock all the classes/dependencies inside it except the class we are testing and control all the functions inside those classes.
-  You should have a similar folder structure inside the *test* folder i.e. mimic the *lib* folder structure inside the *test* folder.

>  You should have basic knowledge on dart and abstraction

## Domain Layer

It is one of the most important layers. It should remain unchanged even if all other layer changes. We should always start with the domain layer.

The domain layer consists of entities, repositories, and use cases.

![](https://cdn-images-1.medium.com/max/2000/1*LxYD7KSKLkGj3C2hD2DzIg.png)

### Entities

It is a lightweight domain object and the basic data structure that we will absolutely need.

Eg. Entity of Movie *lib/features/movie/domain/entities/movie_entity.dart*

   

```
 import 'package:equatable/equatable.dart';

    class MovieEntity extends Equatable {
      final String movieId;
      final String title;
      final String thumbnail;
      final String movieUrl;
      final bool unlocked;
      const MovieEntity({
        required this.movieId,
        required this.title,
        required this.thumbnail,
        required this.movieUrl,
        required this.unlocked,
      });

    @override
      List<Object> get props {
        return [
          movieId,
          title,
          thumbnail,
          movieUrl,
          unlocked,
        ];
      }
    }
```
>  [Equatable ](https://pub.dev/packages/equatable)overrides == and hashCode for you so you don't have to waste your time writing lots of boilerplate code.

### Repositories

It is in both the domain and data layer. Its definition is in the domain layer and implementation is in the data layer allowing total independence of the domain layer.

Eg. Repository of Movie *lib/features/movie/domain/repositories/movie_repository.dart*

```
  import 'package:dartz/dartz.dart';
    import '../../../../core/error/failure.dart';
    import '../entities/movie_entity.dart';
    abstract class MovieRepository {
      Future<Either<Failure, List<MovieEntity>>> getMovieList();
    }
```

It will have a method to get a list of movies, i.e. list of movie entities. _Future<Either<Failure, List<MovieEntity>>>_ will ensure error handling . Let us move on. Wait a minute what is _either_ keyword? It allows a function to return between failure and List of movie entities, not both. It has been obtained from package _Dartz_ which gives functional programming capabilities to dart. _Failure_ class was defined in the _core/error_ file to define the failure of the code.
>  In core the features, abstraction or module that is used in other features are defined

Eg. Failure is defined in *lib/core/error/failure.dart*

    

```
import 'package:equatable/equatable.dart';
    abstract class Failure extends Equatable {
     @override
      List<Object?> get props => [];
    }
    //General Failures
    class ServerFailure extends Failure {}
    class CacheFailure extends Failure {}

```
### Usecases

Use Cases are where the business logic gets executed. It calls a repository to get data.

Eg. Usecase to get movie list *lib/features/movie/domain/usecases/get_movie_list_usecase.dart*

```
    import 'package:dartz/dartz.dart';
    import '../../../../core/error/failure.dart';
    import '../../../../core/usecases/usecase.dart';
    import '../entities/movie_entity.dart';
    import '../repositories/movie_repository.dart';

    class GetMovieListUsecase implements UseCase<List<MovieEntity>, NoParams> {
      final MovieRepository movieRepository;
      GetMovieListUsecase({
        required this.movieRepository,
      });

    @override
      Future<Either<Failure, List<MovieEntity>>> call(NoParams params) async {
        return await movieRepository.getMovieList();
      }
    }

```
*GetVideoLiseUsecase* implements use-case inside *lib/core/use-case* . We are defining rules for use cases there.

Eg. Base use-case is defined in *lib/core/usecases/usecase.dart*

   

```
 import 'package:dartz/dartz.dart';
    import 'package:equatable/equatable.dart';
    import '../error/failure.dart';
    abstract class UseCase<Type, Params> {
      Future<Either<Failure, Type>> call(Params params);
    }

    class NoParams extends Equatable {
      @override
      List<Object?> get props => [];
    }

```
![](https://cdn-images-1.medium.com/max/2000/1*7g4dzGioUVJGb7Qv8-7SOg.gif)

**Wait a minute this was supposed to be TDD Clean Architecture. Shouldn’t we write a test first?**

Yes, before writing a use case, we should first write the test first. Just imagine the above code hasn’t been written yet for now and let’s write a test.
>  Test file should end with ‘_test.dart’ and you can run test by typing ***flutter test*** in terminal or you can run them manually clicking run. While naming the test give it the same name as the file you are trying to test and add ‘ _test ’ to it

Eg. Test for use-case *test/features/movie/domain/usecases/get_movie_list_usecase_test.dart*

   

```
 import 'package:dartz/dartz.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mocktail/mocktail.dart';

class MockMovieRepository extends Mock implements MovieRepository {}
  void main() {
      late GetMovieListUsecase getMovieListUsecase;
      late MockMovieRepository mockMovieRepository;
      final tMovieList = [
        const MovieEntity(
          movieId: 'movieId',title: 'title',thumbnail: 'thumbnail',
      movieUrl: 'movieUrl',unlocked: true,
        ),
        const MovieEntity(
          movieId: 'movieIds',title: 'titles',thumbnail: 'thumbnails',
      movieUrl: 'movieUrls',unlocked: false,
        )   
      ];
      setUp(() {
        mockMovieRepository = MockMovieRepository();
        getMovieListUsecase =
            GetMovieListUsecase(movieRepository: mockMovieRepository);
      });
      test(
        'should get list of movie',
        () async {
          // arrange
          when(() => mockMovieRepository.getMovieList())
              .thenAnswer((_) async => Right(tMovieList));

    // act
          final result = await getMovieListUsecase(NoParams());

    // assert
          verify(() => mockMovieRepository.getMovieList());

    expect(result, Right(tMovieList));
          verifyNoMoreInteractions(mockMovieRepository);
        },
      );
    }

```
Now let’s explain the code

We are testing *GetMovieListUsecase* in *get_movie_list_usecase.dart* which implements a base use case in *lib/core/usecases/usecase.dart* . _GetMovieListUsecase_ use case calls a repository for data, so the repository must be mocked to test the use case. After mocking the repository, we write the test.

Now just like in the original dart file, there is the main file that will be executed. The main function contains the test.

*First, we define all the dependencies we need.*

_Then we instantiate them in setUp. If we look at the **test** there are three parts._

_In **arrange** we control the outcome of the functions._

_In **act**, we run the functions._

_In **assert**, we verify/check if the test is successful or not. **verify** function is making sure that the function has been executed. expect to compare the result and **verifyNoMoreInteractions** to ensure that the repository is no longer being used or interacted with._

If we look closely, we can see that the test looks like documentation explaining to us what the code is supposed to do.
>  **_You might be wondering if usecase hasn’t been created yet how do we map or visualize the test. Here comes, abstraction to the rescue . Even though getMovieListUsecase hasn’t been created , the usecase has been defined using abstract class. We can use this abstract class to visualize what the usecase class will be like and use it to write the test(most of the time). So interface/abstract class are very important in TDD. After writing the test, we write the usecase until the test in successful and refactor it as needed._**

Only use-case needs to be tested in the domain layer as the repository and entity have nothing to test as they are very simple.

Finally, we have the following file and folder

| ![Domain Layer in lib](https://cdn-images-1.medium.com/max/2000/1*Olkcfu9WbtcANI-SVN7-Gg.png)|
|:--:|
| <b>Domain Layer in lib</b>|

| ![Domain Layer in test](https://cdn-images-1.medium.com/max/2000/1*KdK_YDCnuGPEFsjRfIr9bw.png)|
|:--:|
| <b>Domain Layer in test</b>|

In my next post, we will implement TDD for the Data layer.