Access ViewModel inside Testcase using Hilt - unit-testing

Can anyone suggest me that how can I access ViewModel inside Test case using Hilt?
ViewModel class:
#HiltViewModel
class BaseViewModel #Inject constructor(private val repository: BaseRepository) : ViewModel()
AppModule for Hilt
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
fun provideBaseApi(
remoteDataSource: RemoteDataSource
): BaseApi {
return remoteDataSource.buildApi(BaseApi::class.java)
}
Test case file
#SmallTest
#HiltAndroidTest
class BaseViewModelTest {
#get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
#get:Rule(order = 1)
val activityRule = ActivityScenarioRule(MainActivity::class.java)
#Inject
lateinit var baseRepository: BaseRepository
#BindValue
#JvmField
var viewModel = mockk<BaseViewModel>(relaxed = true)
#Before
fun init(){
hiltRule.inject()
}
It is not giving any access to mutable data of ViewModel class.
Thank you in advance.

Related

How to use mockito to test retrofit2 response?

With mockito, I want to test a retrofit response.
Can you guide me in this..
I am new in unit testing with mockito so if you can guide me try to explain your answer a little bit.
viewModel
fun loginUser() {
repository.loginUser(this, "john#mail.com", "changeme2")
}
Repository class
open class LoginRepository #Inject constructor() {
fun loginUser(loginResponse: LoginResponse, email: String, password: String) {
val result = RetrofitClient.getAPI(RetrofitClient.getInstance())
var call = result.loginUser(email, password)
call.enqueue(object : Callback<JsonObject> {
override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
loginResponse.onSuccess()
}
override fun onFailure(call: Call<JsonObject>, t: Throwable) {
loginResponse.onFail()
}
})
}
}
Test class
#RunWith(MockitoJUnitRunner::class)
class LoginViewModelMockTest {
lateinit var loginViewModel: LoginViewModel
lateinit var repository: LoginRepository
lateinit var loginResponse: LoginResponse
#Before
fun setUp() {
loginResponse=mock(LoginResponse::class.java)
repository = mock(LoginRepository::class.java)
loginViewModel = LoginViewModel(repository)
}
#Test
fun loginUser_loginUserCalled_onSuccessShouldCalled() {
loginViewModel.loginUser()
`when`(repository.loginUser(loginResponse, "john#mail.com", "changeme2")).then{
(it.getArgument(2) as LoginResponse).onSuccess()
}
verify(loginResponse).onSuccess()
}
}

Unit test with Flow and Transformations.map

I'm working on unit tests and I have trouble with Flow and Transformations.map.
I think this might be a problem of observer but I'm not sure.
This is the functions of my ViewModel I would like to test
val allPropertiesLiveData: LiveData<List<MapsViewStateItem>> =
Transformations.map(propertyRepository.getAllPropertiesComplete().asLiveData(), ::filterProperty)
private fun filterProperty(properties: List<PropertyWithProximity>?): List<MapsViewStateItem> {
val newList = mutableListOf<MapsViewStateItem>()
properties?.forEach { p ->
if (p.property.dateSold == null)
newList.add(
MapsViewStateItem(
p.property.idProperty,
p.typeOfProperty.nameType,
p.property.price,
p.photos[0],
p.property.adress
)
)
}
return newList
}
However, in the test, I can't get any value for the resulting liveData
This is my test class
class MapsViewModelTest2 {
private val testDispatcher = StandardTestDispatcher()
lateinit var viewModel: MapsViewModel
#Mock
lateinit var propertyRepository: PropertyRepository
#Mock
lateinit var navigationRepository: NavigationRepository
#get:Rule
var instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()
#Mock
private lateinit var mockObserver: Observer<List<MapsViewStateItem>>
#Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
MockitoAnnotations.openMocks(this)
}
#Test
fun getAllPropertiesLiveData2() = runTest {
viewModel = MapsViewModel(propertyRepository, navigationRepository)
val flow = flow {
emit(FakeDatas.fakePropertiesCompletes)
}
Mockito.`when`(propertyRepository.getAllPropertiesComplete()).thenReturn(flow)
viewModel.allPropertiesLiveData.observeForever(mockObserver)
assertEquals(FakeDatas.fakePropertiesCompletes.size,
viewModel.allPropertiesLiveData.value?.size)
viewModel.allPropertiesLiveData.removeObserver(mockObserver)
}
}
And the error is
expected:<2> but was:<null>
Expected :2
Actual :null

How To Test Object expression methods by mocking external dependency which its mothod is using?

While writing unit tests, #InjectMock creates an instance of the class and injects the mocked instances defined using #Mock.
So this works fine when we are testing methods of a class. I have a problem while testing methods of an object expression.
For example:
I have an object class DbService.
object DbService {
private lateinit var connection: Connection
init {
makeConnection()
}
private fun makeConnection(){
Class.forName("com.mysql.cj.jdbc.Driver")
try{
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/Users", "user", "password")
}catch (ex: Exception){
println(ex)
}
}
fun checkConnection(){
var preparedStatement: PreparedStatement = connection.prepareStatement("SHOW DATABASES;")
var resultSet = preparedStatement.executeQuery()
while(resultSet.next()){
println(resultSet.getObject(1))
}
}
}
Now I have to test checkConnection function. How can I do so?
Actually doing some more research, I got some hints and it worked for me.
It's like, you can do manual dependency injection using the setter method.
setter-based Dependency Injection
so what I did, I added one more function to manually set the connection object like this.
object DbService {
private lateinit var connection: Connection
init {
makeConnection()
}
private fun makeConnection(){
Class.forName("com.mysql.cj.jdbc.Driver")
try{
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/Users", "user", "password")
}catch (ex: Exception){
println(ex)
}
}
// setter based dependency injection <----
fun initializeConnectionObject(conn: Connection){
connection = conn
}
fun printingSomeMoreData(){
var statement: Statement
var resultSet: ResultSet
var preparedStatement: PreparedStatement = connection.prepareStatement("SELECT * FROM titles where title=?;")
preparedStatement.setString(1, "Engineer")
resultSet = preparedStatement.executeQuery()
val columnCount = resultSet.metaData.columnCount
while(resultSet.next()){
for(i in 1..columnCount){
print(resultSet.getObject(i).toString() + " ")
}
println()
}
}
}
with the tests like:
#RunWith(MockitoJUnitRunner::class)
class DbServiceTest {
private lateinit var dbService: DbService
#Mock lateinit var mockedConnection: Connection
#Mock lateinit var mockedPreparedStatement: PreparedStatement
#Mock lateinit var mockedResultSet: ResultSet
#Mock
lateinit var mockedResultSetMetaData: ResultSetMetaData
#Before
fun setUp(){
MockitoAnnotations.initMocks(true)
}
#Test
fun printingSomeMoreDataTest() {
dbService = DbService
Mockito.`when`(mockedResultSetMetaData.columnCount).thenReturn(3)
Mockito.`when`(mockedResultSet.next()).thenReturn(true, true, true, true, false)
Mockito.`when`(mockedResultSet.metaData).thenReturn(mockedResultSetMetaData)
Mockito.`when`(mockedResultSet.getObject(1)).thenReturn("11", "21", "31", "41")
Mockito.`when`(mockedResultSet.getObject(2)).thenReturn("12", "22", "32", "42")
Mockito.`when`(mockedResultSet.getObject(3)).thenReturn("13", "23", "33", "43")
Mockito.`when`(mockedPreparedStatement.executeQuery()).thenReturn(mockedResultSet)
Mockito.`when`(mockedConnection.prepareStatement(Mockito.anyString())).thenReturn(mockedPreparedStatement)
dbService.initializeConnectionObject(mockedConnection) // <----
dbService.printingSomeMoreData()
Mockito.verify(mockedResultSet, times(12)).getObject(anyInt())
}
}

Test CoroutineScope infrastructure in Kotlin

would someone be able to show me how to make the getMovies function in this viewModel testable? I can't get the unit tests to await the coroutines properly..
(1) I'm pretty sure I have to create a test-CoroutineScope and a normal lifeCycle-CoroutineScope, as seen in this Medium Article.
(2) Once the scope definitions are made, I'm also unsure how to tell getMovies() which scope it should be using given a normal app context or a test context.
enum class MovieApiStatus { LOADING, ERROR, DONE }
class MovieListViewModel : ViewModel() {
var pageCount = 1
private val _status = MutableLiveData<MovieApiStatus>()
val status: LiveData<MovieApiStatus>
get() = _status
private val _movieList = MutableLiveData<List<Movie>>()
val movieList: LiveData<List<Movie>>
get() = _movieList
// allows easy update of the value of the MutableLiveData
private var viewModelJob = Job()
// the Coroutine runs using the Main (UI) dispatcher
private val coroutineScope = CoroutineScope(
viewModelJob + Dispatchers.Main
)
init {
Log.d("list", "in init")
getMovies(pageCount)
}
fun getMovies(pageNumber: Int) {
coroutineScope.launch {
val getMoviesDeferred =
MovieApi.retrofitService.getMoviesAsync(page = pageNumber)
try {
_status.value = MovieApiStatus.LOADING
val responseObject = getMoviesDeferred.await()
_status.value = MovieApiStatus.DONE
............
} catch (e: Exception) {
_status.value = MovieApiStatus.ERROR
................
}
}
pageCount = pageNumber.inc()
}
...
}
it uses this API service...
package com.example.themovieapp.network
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kotlinx.coroutines.Deferred
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
private const val BASE_URL = "https://api.themoviedb.org/3/"
private const val API_key = ""
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
interface MovieApiService{
//https://developers.themoviedb.org/3/movies/get-top-rated-movies
//https://square.github.io/retrofit/2.x/retrofit/index.html?retrofit2/http/Query.html
#GET("movie/top_rated")
fun getMoviesAsync(
#Query("api_key") apiKey: String = API_key,
#Query("language") language: String = "en-US",
#Query("page") page: Int
): Deferred<ResponseObject>
}
/*
Because this call is expensive, and the app only needs
one Retrofit service instance, you expose the service to the rest of the app using
a public object called MovieApi, and lazily initialize the Retrofit service there
*/
object MovieApi {
val retrofitService: MovieApiService by lazy {
retrofit.create(MovieApiService::class.java)
}
}
I'm simply trying to create a test which asserts the liveData 'status' is DONE after the function.
Here is the Project Repository
First you need to make your coroutine scope injectable somehow, either by creating a provider for it manually, or using an injection framework like dagger. That way, when you test your ViewModel, you can override the coroutine scope with a test version.
There are a few choices to do this, you can simply make the ViewModel itself injectable (article on that here: https://medium.com/chili-labs/android-viewmodel-injection-with-dagger-f0061d3402ff)
Or you can manually create a ViewModel provider and use that where ever it's created. No matter what, I would strongly advise some form of dependency injection in order to achieve real testability.
Regardless, your ViewModel needs to have its CoroutineScope provided, not instantiate the coroutine scope itself.
In other words you might want
class MovieListViewModel(val couroutineScope: YourCoroutineScope) : ViewModel() {}
or maybe
class MovieListViewModel #Inject constructor(val coroutineScope: YourCoroutineScope) : ViewModel() {}
No matter what you do for injection, the next step is to create your own CoroutineScope interface that you can override in the test context. For example:
interface YourCoroutineScope : CoroutineScope {
fun launch(block: suspend CoroutineScope.() -> Unit): Job
}
That way when you use the scope for your app, you can use one scope, say, lifecycle coroutine scope:
class LifecycleManagedCoroutineScope(
private val lifecycleCoroutineScope: LifecycleCoroutineScope,
override val coroutineContext: CoroutineContext = lifecycleCoroutineScope.coroutineContext) : YourCoroutineScope {
override fun launch(block: suspend CoroutineScope.() -> Unit): Job = lifecycleCoroutineScope.launchWhenStarted(block)
}
And for your test, you can use a test scope:
class TestScope(override val coroutineContext: CoroutineContext) : YourCoroutineScope {
val scope = TestCoroutineScope(coroutineContext)
override fun launch(block: suspend CoroutineScope.() -> Unit): Job {
return scope.launch {
block.invoke(this)
}
}
}
Now, since your ViewModel is using a scope of type YourCoroutineScope, and since, in the examples above, both the lifecycle and test version implement the YourCoroutineScope interface, you can use different versions of the scope in different situations, i.e. app vs test.
Ok, thanks to Dapp's answer, I was able to write some tests which seem to be awaiting the function Properly.
Here is a copy of what I did :)
enum class MovieApiStatus { LOADING, ERROR, DONE }
class MovieListViewModel(val coroutineScope: ManagedCoroutineScope) : ViewModel() {
//....creating vars, livedata etc.
init {
getMovies(pageCount)
}
fun getMovies(pageNumber: Int) =
coroutineScope.launch{
val getMoviesDeferred =
MovieApi.retrofitService.getMoviesAsync(page = pageNumber)
try {
_status.value = MovieApiStatus.LOADING
val responseObject = getMoviesDeferred.await()
_status.value = MovieApiStatus.DONE
if (_movieList.value == null) {
_movieList.value = ArrayList()
}
pageCount = pageNumber.inc()
_movieList.value = movieList.value!!.toList().plus(responseObject.results)
.sortedByDescending { it.vote_average }
} catch (e: Exception) {
_status.value = MovieApiStatus.ERROR
_movieList.value = ArrayList()
}
}
fun onLoadMoreMoviesClicked() =
getMovies(pageCount)
//...nav functions, clearing functions etc.
}
and here are the test cases
#ExperimentalCoroutinesApi
#RunWith(MockitoJUnitRunner::class)
class MovieListViewModelTest {
#get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
private val testDispatcher = TestCoroutineDispatcher()
private val managedCoroutineScope: ManagedCoroutineScope = TestScope(testDispatcher)
lateinit var viewModel: MovieListViewModel
#Before
fun setup() {
//resProvider.mockColors()
Dispatchers.setMain(testDispatcher)
viewModel = MovieListViewModel(managedCoroutineScope)
}
#After
fun tearDown() {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
#ExperimentalCoroutinesApi
#Test
fun getMoviesTest() {
managedCoroutineScope.launch {
assertTrue(
"initial List, API status: ${viewModel.status.getOrAwaitValue()}",
viewModel.status.getOrAwaitValue() == MovieApiStatus.DONE
)
assertTrue(
"movieList has ${viewModel.movieList.value?.size}, != 20",
viewModel.movieList.value?.size == 20
)
assertTrue(
"pageCount = ${viewModel.pageCount}, != 2",
viewModel.pageCount == 2
)
viewModel.onLoadMoreMoviesClicked()
assertTrue(
"added to list, API status: ${viewModel.status.getOrAwaitValue()}",
viewModel.status.getOrAwaitValue() == MovieApiStatus.DONE
)
assertTrue(
"movieList has ${viewModel.movieList.value?.size}, != 40",
viewModel.movieList.value?.size == 40
)
}
}
}
It took some trial and error playing around with the Scopes.. runBlockingTest{} was causing an issue 'Exception: job() not completed'..
I also had to create a viewModel factory in order for the fragment to create the viewModel for when the app is running normally..
Project Repo

repository get an instance of a context from a bootstrap container

the repository is a prop of an Mvc controller, i'm trying to write a test method to check this controller,
but i get an error in the container call...
i'm new in mvc and testing.. so i dont know where to start
how can i do this?
this is how the test looks like:
public void SomeTest()
{
var controller= new SomeController();
var result = SomeController.Index();
Assert.IsNotNull(result);
}
The error i recive when i run the test
an exception of type System.NullReferenceException occurred in SomeContext.dll but was not handled in user code
Has your repository been initialized?
In your controller:
private Repository Repository {get;set;}
public ActionResult Index()
{
Repository = new Repository();
var something = Repository.DoSomeWork();
return View(something);
}
In your test class:
public void SomeTest()
{
var controller = new SomeController();
var result = controller.Index();
Assert.IsNotNull(result);
}
or if you are using dependency injection, with Ninject property injection you can try using Moq to inject the class:
public class SomeController : Controller
{
private IRepository repository;
[Inject]
public IRepository Repository
{
get { return repository; }
set { repository = value; }
}
// GET: /Some/
public ActionResult Index()
{
var someCollection = Repository.SomeMethod("some parameter");
foreach (var value in someCollection)
{
ViewData["message"] += value;
}
return View(someCollection);
}
}
and the test class with moq:
public class SomeTestClass
{
private Mock<IRepository> mockRepository;
[Test]
public void GivenSometestThenExpectSomeResult()
{
// Arrange
var controller = new SomeController();
mockRepository = new Mock<IRepository>();
mockRepository.Setup(x => x.SomeMethod(It.IsAny<string>())).Returns(new List<string>());
controller.Repository = mockRepository.Object;
// Act
ActionResult result = controller.Index();
// Assert
Assert.AreEqual("Index", result.ViewName);
}
}