Transcripted Summary

So, we just saw how testing the business logic in models works.

This is a pure unit test. There is no mocking needed. You invoke the model, and we were able to use it directly.

But a lot of times in unit testing, you need to do mocking.



There are advanced concepts like fake or test doubles, but mock is a starting point.

# What is Mocking?

Let me tell you what a mock is.

Instead of you calling the actual class, an actual business logic in the app, you create a separate instance of it.

You separate instance of the same class, but you tell it what to do. You control the class — exactly what you want it to do — so that you can assert the other things. I's a way of giving you control.

Let me show you what exactly it means in the code.

So, we have the HouseViewModel (house_view_model.dart) and this has a little bit of logic.



It's calling the repository.

When the repository is called and it is done, you see the loaded status. And if there is an is error, you see the error status.

But if you want to test this ViewModel, instead of calling the exact logic, you will call the MockViewModel.

For doing mocking in Flutter and Dart, you use the library called Mockito.

# Using Mockito for Unit Tests in Flutter

Let me show how to add Mockito.

Because this is a testing only thing, and you don't want it in the package that goes to the app store, you add a dev dependency (Dart: Add Dev Dependency).



It's not an app dependency. So, you say there, “mockito”.



This will be only added in the dev dependency.

Mockito needs something called a build_runner.

So, let's also add that one.



build_runner is needed for you to generate the mocks.

It will generate looking at your core classes, and the mocks will be created.

Let me show you what it means.

# Setting up the Invocations

Let's create a _New File _first. We create “house_view_model_test.dart”.

Just like what we did before, we create a main function and test('Testing view model loaded state', () {}. We create the function and then the function body.

Okay. But let's add the imports. Import the library.



So, this is the syntax for Mockito.

You need to add annotation GenerateMocks

So, there is @GenerateMocks(classes) and you add the classes that you want to add to create the mocks.

We are going to add for the HouseViewModel so let's create that one.



You just tell the name of it.

Then go to the terminal and run the build_runner command — flutter pub run build_runner build.

build_runner is using a lot of context but in this case, what it's doing, is generating a mock class.

So now, after this is done, you will see a mock class being created for this. This is the class that is generated (“house_view_model_test.mocks.dart”).



** Typically, these files that are generated, you do not want to edit them by hand.**

Whatever is in the ViewModel is similar to what you have here but you can tell it exactly what you want it to do.

They give you the ability to modify the mock. You don't want to do this on the production app, but you want to do it on the test app.

That's why we create this mock.

So, let's initialize it.

Let's say var houseViewModel = MockHouseViewModel();



This is important here.

We are only instantiating the mock one, not the actual one.

And we do this method — when(houseViewModel.fetchAllHouses()).

Now we can control what we want to send. So, we'll say, .thenAnswer. Okay, let's do the static import first, it's easier.

We don't need the invocation.



So this underscore — (_) — is a shortcut, if you don't want to use whatever is coming from the function argument. We don't necessarily need to use it. So, I'm just like, "Okay, we don't need to use that".

Let's say that’s a Dart best practice.

Okay. So we say — .thenAnswer((_) => Future.value()) — and that's it.

Let's just stop it there.


([HouseViewModel])
main() {
  test('Testing view model loaded state', () {
    var houseViewModel = MockHouseViewModel();
    when(houseViewModel.fetchAllHouses()).thenAnswer((_) => Future.value());
  });
}

So, you might ask how do you know what we exactly need?

# Testing the ViewModel

Let's go to the ViewModel and see what it is returning.



So here it's returning a Feature<void>. This method is returning a Feature<void>.

We are doing the same thing here in our mock, but we are making sure to give it back.

We are also manipulating the houses, so let's also create a house.

But let's do it on the fly — var house = House(houseName: 'first', score: 23);

The number 23 should not be quotes. It should be an integer (int). Okay.

There is one more thing I wanted to mock because this also has the AsyncDataStatus status.



This is the one actually returning the data back so, we also need to mock that one.

Let's do that.

Let's do @GenerateMocks([HouseViewModel, AsyncData])

Before, we mocked the function. Now we are going to mock the property, the AsyncDataStatus().thenAnswer.

So, this is static typing. So, it shows error without compiling.



This is not going to return a Future.value. It should return an AsyncData type.

So, what it'll return is AsyncData.loaded(house). It can be any data, but we are returning back “house”.

This is all the setup part.

We are so far just doing the setup part (//Arrange).

For the //Assert part all you want to do is, when we call the actual ViewModelhouseViewModel.fetchAllHouses(); — something is called.

Then for the //Act we put verify(houseViewModel.fetchAllHouses()).called(1);

What we have now is we are just making sure that the fetchAllHouses is called just once.

This is a syntax, verify is kind of an assert.



This is all coming from the mock directory.

We can also verify. We can also assert.

We can do this assert syntax — assert(houseViewModel.asyncDataStatus.toString() == AsyncData.loaded(house));

So here, what we're doing is making sure the “house”, when that is set up above is the same thing.



We asked it to return this one and we are verifying that it's returned it back.

Let's run the test.

Okay. Yeah. It failed.

Sometimes it's puzzling, but why it failed is because this house value that we sent it in and then the one that we are actually accepting here, this is a new instance of it.

So that's why it's not working. What we can do is, there's already a .toString() method on it. You verify the .toString() value.


([HouseViewModel, AsyncData])

main() {
  test('Testing view model loaded state', () {

    //Arrange
    var houseViewModel = MockHouseViewModel();
    var house = House(houseName: 'first', score: 23);
    when(houseViewModel.fetchAllHouses()).thenAnswer((_) => Future.value());
    when(houseViewModel.asyncDataStatus)
        .thenAnswer((_) => AsyncData.loaded(house));

    //Act
    houseViewModel.fetchAllHouses();

    //Assert
    verify(houseViewModel.fetchAllHouses()).called(1);
    assert(houseViewModel.asyncDataStatus.toString() ==
        AsyncData.loaded(house).toString());
  });
}

Let's run this now.

Another thing to note, it magically came back into the second line and also formatted very nicely.



That's a Dart thing. The Dart format that is already included in the framework.

When I hit enter on a command, the format already runs.

This is the basic setup of how you can do mocking.

You can go much deeper. For example, you can go and mock the repository and the houses’ values and make sure that all the values that are sent from the repository are coming back into the ViewModel.

So, we can go much deeper. But this is the basic setup.

We talked about Mockito and the build_runner. I showed how to do the when and thenAnswers and the basic way of testing the ViewModel.

That's it for this chapter. We covered a lot of ground; let’s go for the quiz.



Resources



© 2024 Applitools. All rights reserved. Terms and Conditions Privacy Policy GDPR