complete test BoardRepository
All checks were successful
continuous-integration/drone/push Build is passing

- refactoring BoardRepository for testing
This commit is contained in:
Raffaele Mignone 2019-12-04 18:35:17 +01:00
parent 44775d0619
commit 6d45b0bfae
Signed by: norangebit
GPG Key ID: F5255658CB220573
4 changed files with 355 additions and 191 deletions

View File

@ -41,11 +41,15 @@ class BoardRepository(
override fun onResponse(
call: Call<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>,
response: Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>
) = synchronizeCallback(response)
) {
CoroutineScope(Dispatchers.IO).launch {
synchronizeCallback(response)
}
}
})
}
private fun synchronizeCallback(
private suspend fun synchronizeCallback(
response: Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>
) {
if (!response.isSuccessful) {
@ -81,11 +85,15 @@ class BoardRepository(
override fun onResponse(
call: Call<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>,
response: Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>
) = insertBoardCallback(response, title)
) {
CoroutineScope(Dispatchers.IO).launch {
insertBoardCallback(response, title)
}
}
})
}
private fun insertBoardCallback(
private suspend fun insertBoardCallback(
response: Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>,
title: String
) {
@ -101,10 +109,8 @@ class BoardRepository(
return
}
CoroutineScope(Dispatchers.IO).launch {
dao.insert(Board(board.id, title))
}
}
fun deleteBoard(id: String) {
service.deleteBoard(id).enqueue(
@ -116,40 +122,38 @@ class BoardRepository(
override fun onResponse(
call: Call<Void>,
response: Response<Void>
) = deleteBoardCallback(response, id)
) {
CoroutineScope(Dispatchers.IO).launch {
deleteBoardCallback(response, id)
}
}
})
}
private fun deleteBoardCallback(
private suspend fun deleteBoardCallback(
response: Response<Void>,
id: String
) {
if (!response.isSuccessful) {
logNetworkError("${response.code()}, ${response.message()}")
logNetworkError("${response.code()} ${response.message()}")
return
}
CoroutineScope(Dispatchers.IO).launch {
dao.delete(Board(id))
}
}
private fun addNewBoardToDb(boards: Collection<Board>) {
private suspend fun addNewBoardToDb(boards: Collection<Board>) {
boards.forEach {
CoroutineScope(Dispatchers.IO).launch {
dao.insert(it)
}
}
}
private fun removeOldBoardsFromDb(boards: Collection<Board>) {
private suspend fun removeOldBoardsFromDb(boards: Collection<Board>) {
allBoards.value?.minus(boards)
?.forEach {
CoroutineScope(Dispatchers.IO).launch {
dao.delete(it)
}
}
}
private fun logNetworkError(message: String?) {
Log.e("RETROFIT", message)

View File

@ -1,171 +0,0 @@
package it.unisannio.ding.ids.wedroid.app
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import it.unisannio.ding.ids.wedroid.app.data.dao.BoardDao
import it.unisannio.ding.ids.wedroid.app.data.entity.Board
import it.unisannio.ding.ids.wedroid.app.data.repository.BoardRepository
import it.unisannio.ding.ids.wedroid.app.util.PreferenceReader
import it.unisannio.ding.ids.wedroid.wrapper.api.BoardService
import it.unisannio.ding.ids.wedroid.wrapper.entity.BoardBackgroundColor
import junit.framework.TestCase.*
import kotlinx.coroutines.runBlocking
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Test
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.net.HttpURLConnection
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.jvm.isAccessible
class BoardRepositoryTest {
private val reader = mockk<PreferenceReader>()
private val webServer = MockWebServer()
private lateinit var service: BoardService
private lateinit var latch: CountDownLatch
@Before
fun setUp() {
webServer.start()
service = Retrofit.Builder()
.baseUrl(webServer.url("/"))
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(BoardService::class.java)
every { reader.userId } returns "user id"
latch = CountDownLatch(1)
}
@After
fun teardown() {
webServer.close()
}
@Test
fun insertWithOkOnServer() {
val dao = mockk<BoardDao>()
var inInsert = false
coEvery {
dao.insert(any())
} answers {
inInsert = true
assertEquals("id", arg<Board>(0).id)
assertEquals("title", arg<Board>(0).title)
latch.countDown()
}
webServer.enqueue(
MockResponse()
.setResponseCode(HttpURLConnection.HTTP_OK)
.setBody("{ \"_id\": \"id\" }")
)
val repository = BoardRepository(
dao, service, reader
)
repository.insertBoard(
"title", true, BoardBackgroundColor.LIMEGREEN
)
latch.await(5, TimeUnit.SECONDS)
assertTrue(inInsert)
}
@Test
fun notInsertWithErrorOnServer() {
val dao = mockk<BoardDao>()
var inInsert = false
coEvery {
dao.insert(any() as Board)
} answers {
inInsert = true
latch.countDown()
}
webServer.enqueue(
MockResponse()
.setResponseCode(HttpURLConnection.HTTP_BAD_REQUEST)
)
val repository = BoardRepository(
dao, service, reader
)
repository.insertBoard(
"title", true, BoardBackgroundColor.LIMEGREEN
)
latch.await(3, TimeUnit.SECONDS)
assertFalse(inInsert)
}
@Test
fun deleteWithOkOnServer() {
val dao = mockk<BoardDao>()
var inDelete = false
coEvery {
dao.delete(any() as Board)
} answers {
assertEquals("id", arg<Board>(0).id)
inDelete = true
latch.countDown()
}
webServer.enqueue(
MockResponse()
.setResponseCode(HttpURLConnection.HTTP_OK)
)
val repository = BoardRepository(
dao, service, reader
)
repository.deleteBoard("id")
latch.await(5, TimeUnit.SECONDS)
assertTrue(inDelete)
}
@Test
fun notDeleteWithErrorOnServer() {
val dao = mockk<BoardDao>()
var inDelete = false
coEvery {
dao.delete(any() as Board)
} answers {
assertEquals("id", arg<Board>(0).id)
inDelete = true
latch.countDown()
}
webServer.enqueue(
MockResponse()
.setResponseCode(HttpURLConnection.HTTP_BAD_REQUEST)
)
val repository = BoardRepository(
dao, service, reader
)
repository.deleteBoard("id")
latch.await(3, TimeUnit.SECONDS)
assertFalse(inDelete)
}
}

View File

@ -0,0 +1,13 @@
package it.unisannio.ding.ids.wedroid.app
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.jvm.isAccessible
fun getPrivateFun(name: String, kClass: KClass<*>): KFunction<*>? {
return kClass.declaredFunctions
.find { it.name == name }
.also { it?.isAccessible = true }
}

View File

@ -0,0 +1,318 @@
package it.unisannio.ding.ids.wedroid.app.data.repository
import android.util.Log
import androidx.lifecycle.LiveData
import io.mockk.*
import it.unisannio.ding.ids.wedroid.app.data.dao.BoardDao
import it.unisannio.ding.ids.wedroid.app.data.entity.Board
import it.unisannio.ding.ids.wedroid.app.getPrivateFun
import it.unisannio.ding.ids.wedroid.app.util.PreferenceReader
import it.unisannio.ding.ids.wedroid.wrapper.api.BoardService
import junit.framework.TestCase.*
import kotlinx.coroutines.*
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Test
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import kotlin.reflect.full.callSuspend
class BoardRepositoryTest {
private val reader = mockk<PreferenceReader>()
private val webServer = MockWebServer()
private lateinit var service: BoardService
@Before
fun setUp() {
webServer.start()
service = Retrofit.Builder()
.baseUrl(webServer.url("/"))
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(BoardService::class.java)
mockkStatic(Log::class)
every { reader.userId } returns "user id"
every { Log.e(any(), any()) } returns 0
}
@After
fun teardown() {
webServer.close()
}
@Test
fun addNewBoardsToDb() {
val dao = mockk<BoardDao>()
var count = 0
coEvery {
dao.insert(any())
} answers {
count++
}
val addNewBoardToDb = getPrivateFun(
"addNewBoardToDb", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
val board0 = Board("id0", "title0")
val board1 = Board("id1", "title1")
val board2 = Board("id2", "title2")
runBlocking {
addNewBoardToDb?.callSuspend(
repository,
listOf(board0, board1, board2)
)
}
coVerifyAll {
dao.insert(board0)
dao.insert(board1)
dao.insert(board2)
}
assertEquals(3, count)
}
@Test
fun insertBoardCallbackSuccess() {
val dao = mockk<BoardDao>()
val response =
mockk<Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>()
val board = mockk<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>()
every { response.isSuccessful } returns true
every { response.body() } returns board
every { board.id } returns "id"
coEvery { dao.insert(any()) } answers {}
val insertBoard = getPrivateFun(
"insertBoardCallback", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
insertBoard?.callSuspend(
repository,
response,
"title"
)
}
coVerify { dao.insert(Board("id", "title")) }
}
@Test
fun insertBoardCallbackEmptyBody() {
val dao = mockk<BoardDao>()
val response =
mockk<Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>()
every { response.isSuccessful } returns true
every { response.body() } returns null
val insertBoard = getPrivateFun(
"insertBoardCallback", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
insertBoard?.callSuspend(
repository,
response,
"title"
)
}
verify { Log.e("RETROFIT", "empty body") }
}
@Test
fun insertBoardCallbackError() {
val dao = mockk<BoardDao>()
val response =
mockk<Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>()
every { response.isSuccessful } returns false
every { response.code() } returns 400
every { response.message() } returns "Error"
val insertBoard = getPrivateFun(
"insertBoardCallback", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
insertBoard?.callSuspend(
repository,
response,
"title"
)
}
verify { Log.e("RETROFIT", "400 Error") }
}
@Test
fun deleteBoardCallbackSuccess() {
val dao = mockk<BoardDao>()
val response =
mockk<Response<Void>>()
every { response.isSuccessful } returns true
coEvery { dao.delete(any()) } answers {}
val deleteBoard = getPrivateFun(
"deleteBoardCallback", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
deleteBoard?.callSuspend(
repository,
response,
"id"
)
}
coVerify { dao.delete(Board("id")) }
}
@Test
fun deleteBoardCallbackError() {
val dao = mockk<BoardDao>()
val response =
mockk<Response<Void>>()
every { response.isSuccessful } returns false
every { response.code() } returns 400
every { response.message() } returns "Error"
val deleteBoard = getPrivateFun(
"deleteBoardCallback", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
deleteBoard?.callSuspend(
repository,
response,
"id"
)
}
verify { Log.e("RETROFIT", "400 Error") }
}
@Test
fun deleteOldBoardFromDb() {
val dao = mockk<BoardDao>()
val dbBoards = mockk<LiveData<List<Board>>>()
val board0 = Board("id0", "title0")
val board1 = Board("id1", "title1")
val board2 = Board("id2", "title2")
val board3 = Board("id2", "title3")
every { dbBoards.value } returns listOf(
board0, board1, board2, board3
)
coEvery { dao.getAllBoard() } returns dbBoards
coEvery { dao.delete(any()) } answers {}
val removeOldBoards = getPrivateFun(
"removeOldBoardsFromDb", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
removeOldBoards?.callSuspend(
repository,
listOf(
board0, board1, board3
)
)
}
coVerify { dao.delete(board2) }
}
@Test
fun synchronizeCallbackError() {
val dao = mockk<BoardDao>()
val response =
mockk<Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>>()
every { response.isSuccessful } returns false
every { response.code() } returns 400
every { response.message() } returns "Error"
val synchronize = getPrivateFun(
"synchronizeCallback", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
synchronize?.callSuspend(
repository,
response
)
}
verify { Log.e("RETROFIT", "400 Error") }
}
@Test
fun synchronizeCallbackEmptyBody() {
val dao = mockk<BoardDao>()
val response =
mockk<Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>>()
every { response.isSuccessful } returns true
every { response.body() } returns null
val synchronize = getPrivateFun(
"synchronizeCallback", BoardRepository::class
)
val repository = BoardRepository(
dao, service, reader
)
runBlocking {
synchronize?.callSuspend(
repository,
response
)
}
}
}