Updated unit tests

This commit is contained in:
2020-11-20 13:49:55 +00:00
parent 508737fb42
commit 820761b3aa
10 changed files with 82 additions and 41 deletions

View File

@@ -58,6 +58,10 @@ dependencies {
//mock websever for testing retrofit responses //mock websever for testing retrofit responses
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0" testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
// Mockk
def mockk_ver = "1.10.2"
testImplementation "io.mockk:mockk:$mockk_ver"
androidTestImplementation "io.mockk:mockk-android:$mockk_ver"
//mockito and livedata testing //mockito and livedata testing
testImplementation 'org.mockito:mockito-inline:2.13.0' testImplementation 'org.mockito:mockito-inline:2.13.0'
@@ -90,4 +94,7 @@ dependencies {
// Circle Image View // Circle Image View
implementation 'com.mikhaellopez:circularimageview:4.2.0' implementation 'com.mikhaellopez:circularimageview:4.2.0'
// extra
testImplementation 'org.json:json:20180813'
} }

View File

@@ -13,6 +13,7 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.io.IOException import java.io.IOException
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class MoviesRoomDatabaseTest{ class MoviesRoomDatabaseTest{
private lateinit var simpleDao: SimpleDao private lateinit var simpleDao: SimpleDao

View File

@@ -9,7 +9,7 @@ import java.io.IOException
* Abstract class for extracting <T> from Retrofit Response<T> * Abstract class for extracting <T> from Retrofit Response<T>
* Or throw IOException if the API call fails * Or throw IOException if the API call fails
*/ */
abstract class ResponseUnwrap { abstract class SafeApiCall {
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
suspend fun <T : Any> responseUnwrap( suspend fun <T : Any> responseUnwrap(

View File

@@ -1,7 +1,7 @@
package com.example.h_mal.movielisttest.data.repository package com.example.h_mal.movielisttest.data.repository
import com.example.h_mal.movielisttest.data.network.MoviesApi import com.example.h_mal.movielisttest.data.network.MoviesApi
import com.example.h_mal.movielisttest.data.network.networkUtils.ResponseUnwrap import com.example.h_mal.movielisttest.data.network.networkUtils.SafeApiCall
import com.example.h_mal.movielisttest.data.network.response.MoviesResponse import com.example.h_mal.movielisttest.data.network.response.MoviesResponse
import com.example.h_mal.movielisttest.data.network.response.ResultsItem import com.example.h_mal.movielisttest.data.network.response.ResultsItem
import com.example.h_mal.movielisttest.data.prefs.PreferenceProvider import com.example.h_mal.movielisttest.data.prefs.PreferenceProvider
@@ -12,7 +12,7 @@ class RepositoryImpl(
private val api: MoviesApi, private val api: MoviesApi,
private val database: MoviesRoomDatabase, private val database: MoviesRoomDatabase,
private val preferences: PreferenceProvider private val preferences: PreferenceProvider
) : Repository, ResponseUnwrap() { ) : Repository, SafeApiCall() {
override suspend fun getMoviesFromApi(pageNumber: Int): MoviesResponse? { override suspend fun getMoviesFromApi(pageNumber: Int): MoviesResponse? {

View File

@@ -7,7 +7,6 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.SimpleItemAnimator
import com.example.h_mal.movielisttest.R import com.example.h_mal.movielisttest.R
import com.example.h_mal.movielisttest.utils.* import com.example.h_mal.movielisttest.utils.*
import kotlinx.android.synthetic.main.empty_view_item.view.* import kotlinx.android.synthetic.main.empty_view_item.view.*

View File

@@ -9,7 +9,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.example.h_mal.movielisttest.R import com.example.h_mal.movielisttest.R
import com.example.h_mal.movielisttest.data.models.Movie import com.example.h_mal.movielisttest.data.models.Movie
import com.example.h_mal.movielisttest.utils.loadImage import com.example.h_mal.movielisttest.utils.loadImage
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.item_layout.view.* import kotlinx.android.synthetic.main.item_layout.view.*
/** /**

View File

@@ -5,7 +5,6 @@ import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.example.h_mal.movielisttest.R
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
fun View.show() { fun View.show() {

View File

@@ -0,0 +1,52 @@
package com.example.h_mal.movielisttest.data.network.networkUtils
import com.example.h_mal.movielisttest.data.network.response.MoviesResponse
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.ResponseBody.Companion.toResponseBody
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Test
import retrofit2.Response
import java.io.IOException
import kotlin.test.assertFailsWith
class SafeApiCallTest: SafeApiCall(){
@Test
fun successfulResponse_SuccessfulOutput() = runBlocking{
// GIVEN
val mockApiResponse = mockk<MoviesResponse>()
val mockResponse = Response.success(mockApiResponse)
// WHEN
val result = responseUnwrap { mockResponse }
// THEN
assertNotNull(result)
assertEquals(mockApiResponse, result)
}
@Test
fun unsuccessfulResponse_thrownOutput() = runBlocking{
// GIVEN
val errorMessage = "{\n" +
" \"status_code\": 7,\n" +
" \"status_message\": \"Invalid API key: You must be granted a valid key.\",\n" +
" \"success\": false\n" +
"}"
val errorResponseBody = errorMessage.toResponseBody("application/json".toMediaTypeOrNull())
val mockResponse = Response.error<String>(404, errorResponseBody)
//THEN - assert exception is not null
val ioExceptionReturned = assertFailsWith<IOException> {
responseUnwrap { mockResponse }!!
}
assertNotNull(ioExceptionReturned)
assertEquals(ioExceptionReturned.message, "Invalid API key: You must be granted a valid key.")
}
}

View File

@@ -6,7 +6,8 @@ import com.example.h_mal.movielisttest.data.prefs.PreferenceProvider
import com.example.h_mal.movielisttest.data.room.MoviesRoomDatabase import com.example.h_mal.movielisttest.data.room.MoviesRoomDatabase
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okhttp3.ResponseBody import okhttp3.ResponseBody
import org.junit.Assert.* import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.Mock import org.mockito.Mock

View File

@@ -2,16 +2,12 @@ package com.example.h_mal.movielisttest.ui.main
import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.test.espresso.idling.CountingIdlingResource
import com.example.h_mal.movielisttest.application.MovieListApplication.Companion.idlingResources
import com.example.h_mal.movielisttest.data.network.response.MoviesResponse import com.example.h_mal.movielisttest.data.network.response.MoviesResponse
import com.example.h_mal.movielisttest.data.repository.Repository import com.example.h_mal.movielisttest.data.repository.Repository
import com.example.h_mal.movielisttest.data.room.MovieEntity import com.example.h_mal.movielisttest.data.room.MovieEntity
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert.* import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@@ -20,7 +16,8 @@ import org.mockito.Mock
import org.mockito.Mockito import org.mockito.Mockito
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.io.IOException import java.io.IOException
import javax.annotation.meta.When
class MainViewModelTest { class MainViewModelTest {
@@ -32,9 +29,6 @@ class MainViewModelTest {
@Mock @Mock
lateinit var repository: Repository lateinit var repository: Repository
@Mock
lateinit var observer: Observer<List<MovieEntity>>
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
@@ -55,12 +49,9 @@ class MainViewModelTest {
//THEN //THEN
viewModel.loadMovies() viewModel.loadMovies()
delay(200) delay(50)
viewModel.operationState.observeForever{
it.getContentIfNotHandled()?.let {result -> kotlin.test.assertFalse { viewModel.operationState.value?.getContentIfNotHandled()!! }
kotlin.test.assertFalse { result }
}
}
} }
@Test @Test
@@ -70,11 +61,8 @@ class MainViewModelTest {
// THEN // THEN
viewModel.loadMovies() viewModel.loadMovies()
viewModel.operationError.observeForever{ delay(50)
it.getContentIfNotHandled()?.let {result -> assertEquals(viewModel.operationError.value?.getContentIfNotHandled()!!, "throwed")
assertEquals(result, "throwed")
}
}
} }
@Test @Test
@@ -83,30 +71,25 @@ class MainViewModelTest {
val mockApiResponse = Mockito.mock(MoviesResponse::class.java) val mockApiResponse = Mockito.mock(MoviesResponse::class.java)
//WHEN //WHEN
Mockito.`when`(repository.getMoviesFromApi(2)).thenReturn(mockApiResponse)
Mockito.`when`(repository.getCurrentPage()).thenReturn(2) Mockito.`when`(repository.getCurrentPage()).thenReturn(2)
Mockito.`when`(repository.getMoviesFromApi(2)).thenReturn(mockApiResponse)
//THEN //THEN
viewModel.loadMovies() viewModel.loadMoreMovies()
delay(200) delay(50)
viewModel.operationState.observeForever{ kotlin.test.assertFalse { viewModel.operationState.value?.getContentIfNotHandled()!! }
it.getContentIfNotHandled()?.let {result ->
kotlin.test.assertFalse { result }
}
}
} }
@Test @Test
fun getMoreFromRepository_unsuccessfulReturn() = runBlocking{ fun getMoreFromRepository_unsuccessfulReturn() = runBlocking{
// WHEN // WHEN
Mockito.`when`(repository.getCurrentPage()).thenReturn(2)
Mockito.`when`(repository.getMoviesFromApi(2)).thenAnswer{ throw IOException("throwed") } Mockito.`when`(repository.getMoviesFromApi(2)).thenAnswer{ throw IOException("throwed") }
// THEN // THEN
viewModel.loadMovies() viewModel.loadMoreMovies()
viewModel.operationError.observeForever{ delay(50)
it.getContentIfNotHandled()?.let {result -> assertEquals(viewModel.operationError.value?.getContentIfNotHandled()!!, "throwed")
assertEquals(result, "throwed")
}
}
} }
} }