Flutter Test With Mocktail

Created At: 2024-01-25 06:36:30 Updated At: 2024-02-04 20:48:10

Mocktail is better than Mockito. Mocktio got a lot of boiler plate code.

A class depends on a repository . We can use mocktail to create a fake version of the dependency. Our class depends on AuthenticationRepository.

class MockAuthRepo extends Mock implements AuthenticationRepository {}

void main() {
late CreateUser usecase;
late AuthenticationRepository repository;
setUpAll(() {
repository = MockAuthRepo();
usecase = CreateUser(repository);
});
const params = CreateUserParams.empty();
test('should call the [AuthRepo.createUser]', () async {
//arrange
//STUB
when(() => repository.createUser(
name: any(named: 'name'),
avatar: any(named: 'avatar'),
createdAt: any(named: 'createdAt')))
.thenAnswer((_) async => const Right(null));
//act
final result = await usecase(params);
//assert
expect(result, equals(const Right<Failure, void>(null)));
verify(() => repository.createUser(
name: params.name,
avatar: params.avatar,
createdAt: params.createdAt)).called(1);
verifyNoMoreInteractions(repository);
});
}

We need to ask what does our class takes 

How can we create a fake version of our dependency 

We need to control what the dependencies do

Mocktail helps us creating fake versions of dependencies 

A class depends on a repository . We can use mocktail to create a fake version of the dependency. Our class depends on AuthenticationRepository. 

So we create a class name MockAuthRepo and it will extend Mock class. And then eventually implement AuthenticationRepository()

Then we create a function called main(). The entry point of all your test is void main(){}

The test() comes from Flutter test package. The complete body of the test goes inside the test() function. 

Inside the test(), we need to do the below three things 

  1. Arrange (arranging everything for the test)
  2. Act (carrying out actions)
  3. Assert (confirm the test)

Even before we do other things, first we need to create an instance of our usecases class. We need to do it before test() method starts. 

So, right at the beginning of our main(), we need to declare our usecases and dependencies

And then the instantiation of uses cases  happens inside the setUp()

late CreateUser usecase;
late AuthenticationRepository repository;

setUp(() {
repository = MockAuthRepo();
usecase = CreateUser(repository);
});

Here CreateUser is our real usecase and AuthenticationRepository is also the real repository class. But AuthenticationRepository would be instantiated by the MockAuthRepo() class.

Inside the test() function, we first call the when() function. Inside this we would call our repository function. In this case we will call createUser(). Using createUser() we make sure we are calling external world or data source.

We wait for the answer by using thenAnswer() and it's async call.

We use thenReturn() for non-asnyc if this call is non-async.

when(
() => repository.createUser(
createdAt: any(named: 'createdAt'),
name: any(named: 'name'),
avatar: any(named: 'avatar'),
),
).thenAnswer((_) async => const Right(null));

Arranging and stubbing happen at the same time. With the above code what our usecase body should do is ready.

We use await usecase(params) to carry out the call. This is equivalent of calling CreateUser class from presentation layer.  

expect() functions takes the result and check with the expected value. So the second argument does the checking. In this case, we are expecting to get null from the right side so we did 

expect(result, equals(const Right<dynamic, void>(null)));

We need to make sure it call AuthRepo.createUser method

We use verify() to check if our usecase really got called or not. So with verify() function we make sure AuthRepo.createUser() got called. Since we make sure this call is only once, we used called(1).

So basically, verify() function double checks the call history.

class MockAuthRepo extends Mock implements AuthenticationRepository {}

void main() {
late CreateUser usecase;
late AuthenticationRepository repository;

setUp(() {
repository = MockAuthRepo();
usecase = CreateUser(repository);
});

const params = CreateUserParams.empty();
test(
'should call the [AuthRepo.createUser]',
() async {
// Arrange
// STUB
when(
() => repository.createUser(
createdAt: any(named: 'createdAt'),
name: any(named: 'name'),
avatar: any(named: 'avatar'),
),
).thenAnswer((_) async => const Right(null));

// Act
final result = await usecase(params);

// Assert
expect(result, equals(const Right<dynamic, void>(null)));
verify(
() => repository.createUser(
createdAt: params.createdAt,
name: params.name,
avatar: params.avatar),
).called(1);

verifyNoMoreInteractions(repository);
},
);
}

Comment

  • k
    kleger

    2024-02-27 02:48:57

    Great content. Really appreciate your content What is the right right yo use Mocktail with the Dio package?

Add Reviews