How to test viewmodel android

Android Architecture Components: Testing your ViewModel LiveData

Last week I pushed a fork of the Buffer Android Boilerplate to a public repository — the difference with this fork is that it uses the new architecture components (ViewModels in the presentation layer and Room in the cache layer). If you haven’t seen it yet, you can do so here 👉

bufferapp/clean-architecture-components-boilerplate

clean-architecture-components-boilerplate — A fork of our clean architecture boilerplate, this time using the Android…

But we’re not here to talk about the boilerplate itself! Because you’re going to be writing tests for your ViewModels (I hope 😄), we’re here to take a really quick look at two simple ways in which you can verify that your LiveData is being updated based on the operations taking place within your ViewModel — this will allow us to ensure that our ViewModel classes remain functional and maintainable.

To begin with, we’re going to need a ViewModel class to reference when we’re talking about these tests that we’re going to write. I’m going to go ahead and take this one from the boilerplate:

You can see that the ViewModel instance contains a Use Case class called GetBufferoos — some projects that use ViewModels will be communicating directly with a repository, this will work the same way for your tests — you will just need to mock the corresponding class that is handling the retrieval of your data.

There are two ways in which we can verify our ViewModels are triggering the right events for our Views.

Verifying Observer onChanged() events

The first way in which we can test our ViewModels is by using mockito to verify that our observer onChanged() is called when a postValue() method should have been triggered within our ViewModel. For this example let’s take our fetchBufferoos() method from the code snippet above.

We’re going to write a test that verifies that the progress state is triggered within our ViewModel:

This test is doing one simple thing, checking that when we call the fetchBufferoos() method, our livedata is updated with a change event with the ResourceState value of LOADING. But how is this working?

To begin with, you need to add a mock Observer instance to your test class:

You’ll notice that we then use this mock observer in our tests in this line:

If our livedata doesn’t have an observer, then onChanged events will not be emitted — this observer instance allows us to add a mock observer to our livedata so that we can verify the data is changed when we expect it to.

Asserting LiveData values

If we want to assert specific data has been set within our tests, then we can do in a similar way to above. To be honest, these approaches are pretty similar so it’s probably down to personal preference which you use. Our test method only changes slightly:

Читайте также:  Программированный калькулятор для андроид

The only difference within the test method is this:

All we are doing here is fetching the livedata instance from our ViewModel, retrieving its value and asserting that it matches the desired value.

The differences between these two approaches? The main key here is the focus of the test methods.

For example, imagine if we had a fetchBufferoosReturnsBufferoos() test method and we asserted that the data had been changed like so:

Whilst in most cases this ok, some developers may prefer to split this test into two seperate methods:

  • fetchBufferoosReturnsSuccessState() — Asserting the result like so:
  • fetchBufferoosReturnsBufferoos() — Asserting the result like so:

The above methods are a simple introduction to how you can write unit tests for your ViewModel methods. I’m looking forward to sharing more to do with the testing of architecture components as I learn more 🙂

Источник

Testing ViewModels and Activities in Your MVVM App

By Yawei Li & Shaurya Arora

This is part 3 of our 3-part series on implementing MVVM (Model-View-ViewModel) on Android using Google’s ViewModel and LiveData architecture components. In this part, we will take a look at how to write unit tests for an app that uses ViewModel and LiveData.

Read from the beginning if you’re not familiar with MVVM or Google’s architecture components.

Testing the view model

One of the main goals of MVVM is to minimize the complexity of activities and fragments. As a result, most of the logic lives inside the view model. Therefore, it’s especially important to test our view model thoroughly. Here’s a quick recap of our ViewModel class:

We have 3 LiveData s exposed to the activity: teams , shouldShowError and isLoading . isLoading() is initialized to true and changed to false at the end of loadTeams() . This happens regardless of repository.refreshTeams() successfully fetching data from the network, or throwing an exception. In both situations, we set isLoading ’s value to false so that our observing activity can hide the loading animation. Data is fetched from the network and stored locally in the database in a separate DataRepository class. Moreover, we only want to fetch data from the network if it is not already locally present.

Setting up the test

We run our test class with RobolectricTestRunner . This is because ViewModel depends on classes within the Android SDK. We create the necessary variables, as well as mocks for dependencies. Note that, since TeamViewModel actually performs a transformation on repository.teams , we need to make teamListLiveData a spy. If it were just a mock, the transformation wouldn’t be triggered even though viewModel.teams is being observed.

Test 1: isLoading should be false after calling repo.refreshTeams()

In the first test case we can verify that isLoading ’s value starts out as true , and that after calling loadTeams() , the value gets changed to false . We can also verify that calling loadTeams() triggers refreshTeams() inside the repository. The test is wrapped inside runBlocking <> to ensure that all running coroutines get a chance to complete before the test function returns.

Test 2: isError should be true if repo.refreshTeams() throws some exception

For this case, we want to verify that isError is initially set to false , and that it gets changed to true after loadTeams() is called if repo.refreshTeams() threw an exception.

Note: Kotlin allows us to write our test function names as sentences by enclosing them in back-ticks (`). This improves readability in general, and especially when running your test suite.

Test 3: repo.refreshTeams() should be called when no data in the database

In this test case, if data returned from the database is empty, we want to verify that repo.refreshTeams() is called — that is, data is fetched in the network.

Читайте также:  Как восстановить наушники андроид

At the beginning of the test, we simulate the condition of “no data in the database” by setting teamListLiveData ’s value as an empty list. We start observing viewModel.teams via the observeForever() method, which simulates an observer in an active state. This ensures that an update in the value of the LiveData object backing viewModel.teams is prepared to be delivered, hence triggering the map transformation inside the view model. At the end, we also assert that isLoading and isError have the expected values.

Test 4: repo.refreshTeams() should not be called if data already in the database

In this final test, we want to verify that repo.refreshTeams() is never called if the list of teams is already present in the local database.

The structure of this test is quite similar to that of the last test. However, now, we set the value of teamListLiveData to an actual list of teams to simulate data that is already stored in the database. Moreover, we verify that repo.refreshTeams() is never called using the never() invocation.

Testing the activity

Let’s take a quick look at our Activity class before we begin writing tests:

As we can see, the majority of the data and logic is outside the activity. MainActivity is simply observing the data points exposed by TeamViewModel and updating the UI based on updates in those data points. Therefore, to test the activity, we will verify that our UI is in the correct state when LiveData s in the view model deliver updates to MainActivity via the onChanged() method of the Observer s. Note, also, that we have a @TestOnly setter for the viewModel property so that we can inject a mock view model in tests (can be avoided by using a DI framework such as Koin). We moved the subscribeUi() call from onCreate() to onStart() so that in our tests, we have a chance to inject the mock ViewModel object before the Observer s are attached.

Setting up the test

We create variables for an instance of MainActivity , as well as for an instance of ActivityController which will enable us to drive the MainActivity instance through different lifecycle stages. We create mocks for TeamViewModel , as well as for each of the three LiveData s exposed by it ( teams , isLoading and isError ). Finally, we create ArgumentCaptor s for each of the three Observer s in MainActivity , and add in our MockitoRule to initialize all annotated elements.

In the setUp() function, we first invoke onCreate() of the activity (via activityController.create() ). Then we swap out the real view model inside the activity with a mock via the setTestViewModel() function. Only then do we start the activity via activityController.start() .

Calling activityController.start() invokes MainActivity ’s onStart() function, which in turn invokes the subscribeUi() function where all the observe calls take place. We use Mockito’s verify() function on each of the LiveData mocks together with our captors to get the Observer s that are attached inside subscribeUi() .

Tests 1 and 2: initial view states

To start, we can write 2 quick and simple tests to assert that initially, the loading spinner is visible, and the error view and teams RecyclerView are not shown.

Test 3: list of teams should be displayed when successfully retrieved

In this test, we simulate the success case: a valid list of teams is sent through by the view model.

As noted in the setup stage, we use our captors to simulate different test cases. Here, in the success case, we call onChanged() on the Observer for teams with a valid list containing two Team objects. Moreover, we call onChanged() on the Observer s for isLoading and isError with false . Then, we assert that the teams RecyclerView is visible, and the loading spinner and error view are not shown. We also assert that the adapter of that RecyclerView is holding on to a list of teams which is identical to the one that we passed in.

Читайте также:  Как пользоваться облаком яндекс диск андроид

Test 4: error message should be shown if ViewModel encounters exception

Finally, we test the error case: an error message should be displayed if the ViewModel signals an error in retrieving the list of teams.

In this case, we don’t really care whether the Observer for teams is invoked or not. We mostly care that the Observer for isError is invoked with true , and that the Observer for isLoading is invoked with false . We can see in `TeamViewModel` that this is the exact error scenario. Now, we assert that only the error message is visible, and that all other UI elements are not shown.

And that’s how you can test ViewModel s and related activities!

Yawei Li is a Toronto-based Android developer and an avid dog lover.

Shaurya Arora is a Toronto-based Android developer, drummer and prog metal/djent lover.

Источник

Unit Testing for ViewModel

Unit testing, a testing technique using which individual modules are tested to determine if there are any issues by the developer himself. It is concerned with the functional correctness of the standalone modules

So why is writing tests so important?
Our test is actually protecting the implementation, and any future changes that break the code will also break the tests. In other words, we will be notified if there are changes that break the code.

A good architecture separates concerns into components and makes Unit testing become easier.

In this tutorial, I am going to explain How to write a unit test for ViewModel.
The article will implement MVVM and LiveData.

Model-View-ViewModel

  • The View — that informs the ViewModel about the user’s actions
  • The ViewModel — exposes streams of data relevant to the View
  • The Model is where the business logic resides. It also has no knowledge about the ViewModel but rather sends out observable notifications when updated.

View and ViewModel communicates via LiveData

Observer Pattern for updating Activity or Fragment to handle commonly experienced problems.

The important part here is View ( Activity/Fragments), keeping all the logic in ViewModel, also dispatching user actions immediately to ViewModel using LiveData. We should avoid all android related dependencies in ViewModel to be able to test with pure JUnit test.

Let’s get into code:
I have used Dagger to provide dependencies, RxJava for API call and threading, LiveData to notify view.

Please make yourself familiar with the basics of JUnit Testing
@Before, @After,@Test, @Mock,@RunWith, annotation
https://www.vogella.com/tutorials/JUnit/article.html

Before continue reading, I highly recommend you to take a look at the app source code to understand how it works. You can find entire code link on Github ( https://github.com/droiddevgeeks/NewsApp )

InstantTaskExecutorRule
It will tell JUnit to force tests to be executed synchronously, especially when using Architecture Components.

How can we verify our ViewModels are triggering the right events for our Views?

Verifying Observer onChanged() events

We can test our ViewModels is by using mockito to verify that our observer onChanged() is called when a postValue() method should have been triggered within our ViewModel.

I am using mockito to mock dependencies. If you face problem like mockito cant spy final class then use
testImplementation ‘org.mockito:mockito-inline:2.13.0’
in the build.gradle

there is 1 more way if you are using org.mockito:mockito-core
then inside test folder create folder “ resources”
then inside resources folder create new folder “ mockito-extensions”
then inside this folder create file “ org.mockito.plugins.MockMaker” with content “ mock-maker-inline”

Soon I will post more articles on android, core java.
till then happy learning… 🙂

Источник

Оцените статью