add tests for the NetworkCallAdapter

This commit is contained in:
Konrad Pozniak 2020-09-21 19:07:39 +02:00
parent 400acbaf4c
commit 8c9da477f2
8 changed files with 452 additions and 4 deletions

View File

@ -45,6 +45,7 @@ android {
}
sourceSets["main"].java.srcDir("src/main/kotlin")
sourceSets["test"].java.srcDir("src/test/kotlin")
}
tasks {
@ -67,6 +68,7 @@ dependencies {
val retrofitVersion = "2.9.0"
val moshiVersion = "1.10.0"
val daggerVersion = "2.28.3"
val jUnitVersion = "5.7.0"
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.0")
@ -118,5 +120,8 @@ dependencies {
implementation("com.google.dagger:dagger-android-support:$daggerVersion")
kapt("com.google.dagger:dagger-android-processor:$daggerVersion")
testImplementation("junit:junit:4.13")
testImplementation("org.junit.jupiter:junit-jupiter-api:$jUnitVersion")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jUnitVersion")
testImplementation("com.squareup.okhttp3:mockwebserver:$okHttpVersion")
}

View File

@ -19,7 +19,6 @@
package at.connyduck.pixelcat.network.calladapter
import android.util.Log
import okhttp3.Request
import okio.Timeout
import retrofit2.Call
@ -37,7 +36,6 @@ internal class NetworkResponseCall<S : Any>(
override fun onResponse(call: Call<S>, response: Response<S>) {
val body = response.body()
val errorbody = response.errorBody()?.string()
if (response.isSuccessful) {
if (body != null) {
callback.onResponse(
@ -72,7 +70,6 @@ internal class NetworkResponseCall<S : Any>(
}
override fun onFailure(call: Call<S>, throwable: Throwable) {
Log.d("NetworkResponseCall", "Network response failed", throwable)
val networkResponse = when (throwable) {
is IOException -> NetworkResponse.Failure(
NetworkResponseError.NetworkError(

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.network.calladapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.SocketPolicy
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import java.io.IOException
class ApiTest {
private var mockWebServer = MockWebServer()
private lateinit var api: TestApi
@BeforeEach
fun setup() {
mockWebServer.start()
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
api = Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.addCallAdapterFactory(NetworkResponseAdapterFactory())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(OkHttpClient())
.build()
.create(TestApi::class.java)
}
@AfterEach
fun shutdown() {
mockWebServer.shutdown()
}
private fun mockResponse(responseCode: Int, body: String = "") = MockResponse()
.setResponseCode(responseCode)
.setBody(body)
@Test
fun `should return the correct test object`() {
val response = mockResponse(
200,
"""
{
"lets": "not",
"test": 1
}
"""
)
mockWebServer.enqueue(response)
val responseObject = runBlocking {
api.testEndpoint()
}
assertEquals(
NetworkResponse.Success(TestResponseClass("not", 1)),
responseObject
)
}
@Test
fun `should return a ApiError failure when the server returns error 500`() {
val errorCode = 500
val response = mockResponse(errorCode)
mockWebServer.enqueue(response)
val responseObject = runBlocking {
api.testEndpoint()
}
assertEquals(
NetworkResponse.Failure(NetworkResponseError.ApiError(errorCode)),
responseObject
)
}
@Test
fun `should return a NetworkError failure when the network fails`() {
mockWebServer.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST })
val responseObject = runBlocking {
api.testEndpoint()
}
assertEquals(
NetworkResponse.Failure(
NetworkResponseError.NetworkError(
object : IOException() {
override fun equals(other: Any?): Boolean {
return (other is IOException)
}
}
)
),
responseObject
)
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.network.calladapter
import com.squareup.moshi.Types
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import retrofit2.Call
import retrofit2.Retrofit
class NetworkResponseAdapterFactoryTest {
private val retrofit = Retrofit.Builder().baseUrl("http://example.com").build()
@Test
fun `should return a NetworkResponseCallAdapter when the type is supported`() {
val networkResponseType =
Types.newParameterizedType(NetworkResponse::class.java, TestResponseClass::class.java)
val callType = Types.newParameterizedType(Call::class.java, networkResponseType)
val adapter = NetworkResponseAdapterFactory().get(callType, arrayOf(), retrofit)
assertEquals(TestResponseClass::class.java, adapter?.responseType())
}
@Test
fun `should return null if the type is not supported`() {
val adapter = NetworkResponseAdapterFactory().get(TestResponseClass::class.java, arrayOf(), retrofit)
assertNull(adapter)
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.network.calladapter
import okhttp3.ResponseBody.Companion.toResponseBody
import org.junit.Assert.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.IOException
class NetworkResponseCallTest {
private val backingCall = TestCall<String>()
private val networkCall = NetworkResponseCall(backingCall)
@Test
fun `should throw an error when invoking 'execute'`() {
assertThrows<UnsupportedOperationException> {
networkCall.execute()
}
}
@Test
fun `should delegate properties to backing call`() {
with(networkCall) {
assertEquals(isExecuted, backingCall.isExecuted)
assertEquals(isCanceled, backingCall.isCanceled)
assertEquals(request(), backingCall.request())
}
}
@Test
fun `should return new instance when cloned`() {
val clonedCall = networkCall.clone()
assert(clonedCall !== networkCall)
}
@Test
fun `should cancel backing call as well when cancelled`() {
networkCall.cancel()
assert(backingCall.isCanceled)
}
@Test
fun `should parse successful call as NetworkResponse Success`() {
val body = "Test body"
networkCall.enqueue(
object : Callback<NetworkResponse<String>> {
override fun onResponse(
call: Call<NetworkResponse<String>>,
response: Response<NetworkResponse<String>>
) {
assertTrue(response.isSuccessful)
assertEquals(
response.body(),
NetworkResponse.Success(body)
)
}
override fun onFailure(call: Call<NetworkResponse<String>>, t: Throwable) {
throw IllegalStateException()
}
}
)
backingCall.complete(body)
}
@Test
fun `should parse call with 404 error code as ApiError`() {
val errorCode = 404
val errorBody = "not found"
networkCall.enqueue(
object : Callback<NetworkResponse<String>> {
override fun onResponse(
call: Call<NetworkResponse<String>>,
response: Response<NetworkResponse<String>>
) {
assertEquals(
response.body(),
NetworkResponse.Failure(NetworkResponseError.ApiError(errorCode))
)
}
override fun onFailure(call: Call<NetworkResponse<String>>, t: Throwable) {
throw IllegalStateException()
}
}
)
backingCall.complete(Response.error(errorCode, errorBody.toResponseBody()))
}
@Test
fun `should parse call with IOException as NetworkError`() {
val exception = IOException()
networkCall.enqueue(
object : Callback<NetworkResponse<String>> {
override fun onResponse(
call: Call<NetworkResponse<String>>,
response: Response<NetworkResponse<String>>
) {
assertEquals(
response.body(),
NetworkResponse.Failure(NetworkResponseError.NetworkError(exception))
)
}
override fun onFailure(call: Call<NetworkResponse<String>>, t: Throwable) {
throw IllegalStateException()
}
}
)
backingCall.completeWithException(exception)
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.network.calladapter
import retrofit2.http.GET
interface TestApi {
@GET("testpath")
suspend fun testEndpoint(): NetworkResponse<TestResponseClass>
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.network.calladapter
import java.io.InterruptedIOException
import okhttp3.Request
import okio.Timeout
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class TestCall<T> : Call<T> {
private var executed = false
private var canceled = false
private var callback: Callback<T>? = null
private var request = Request.Builder().url("http://example.com").build()
fun completeWithException(t: Throwable) {
synchronized(this) {
callback?.onFailure(this, t)
}
}
fun complete(body: T) = complete(Response.success(body))
fun complete(response: Response<T>) {
synchronized(this) {
callback?.onResponse(this, response)
}
}
override fun enqueue(callback: Callback<T>) {
synchronized(this) {
this.callback = callback
}
}
override fun isExecuted() = synchronized(this) { executed }
override fun isCanceled() = synchronized(this) { canceled }
override fun clone() = TestCall<T>()
override fun cancel() {
synchronized(this) {
if (canceled) return
canceled = true
val exception = InterruptedIOException("canceled")
callback?.onFailure(this, exception)
}
}
override fun execute(): Response<T> {
throw UnsupportedOperationException("Network call does not support synchronous execution")
}
override fun request() = request
override fun timeout() = Timeout()
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.network.calladapter
data class TestResponseClass(
val lets: String,
val test: Int
)