Skip to content

Commit a6f8907

Browse files
committed
feat: Add tests for suspending functions support
1 parent 3d0949b commit a6f8907

File tree

8 files changed

+271
-8
lines changed

8 files changed

+271
-8
lines changed

src/test/kotlin/com/haroldadmin/cnradapter/CancelTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import java.io.IOException
1111
internal class CancelTest : DescribeSpec({
1212

1313
val mockWebServer = MockWebServer()
14-
val factory = CoroutinesNetworkResponseAdapterFactory()
14+
val factory = NetworkResponseAdapterFactory()
1515
val retrofit = Retrofit.Builder()
1616
.baseUrl(mockWebServer.url("/"))
1717
.addConverterFactory(StringConverterFactory())

src/test/kotlin/com/haroldadmin/cnradapter/DeferredTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ internal class DeferredTest : DescribeSpec() {
2525
retrofit = Retrofit.Builder()
2626
.baseUrl(server.url("/"))
2727
.addConverterFactory(StringConverterFactory())
28-
.addCallAdapterFactory(CoroutinesNetworkResponseAdapterFactory())
28+
.addCallAdapterFactory(NetworkResponseAdapterFactory())
2929
.callbackExecutor(executor)
3030
.build()
3131
service = retrofit.create(Service::class.java)
@@ -94,7 +94,7 @@ internal class DeferredTest : DescribeSpec() {
9494
}
9595

9696
context("IO error") {
97-
server.enqueue(MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST))
97+
server.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST })
9898

9999
it("Should be treated as NetworkResponse.NetworkError") {
100100
val response = service.getText().await()
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.haroldadmin.cnradapter
2+
3+
import io.kotlintest.matchers.types.shouldBeSameInstanceAs
4+
import io.kotlintest.matchers.types.shouldBeTypeOf
5+
import io.kotlintest.shouldBe
6+
import okhttp3.Headers
7+
import okhttp3.ResponseBody
8+
import retrofit2.HttpException
9+
import retrofit2.Response
10+
import okhttp3.ResponseBody.Companion.toResponseBody
11+
import org.junit.Test
12+
import retrofit2.Converter
13+
import retrofit2.http.Header
14+
import java.io.IOException
15+
import java.lang.Exception
16+
17+
internal class ErrorExtractionTest {
18+
19+
private val errorConverter = Converter<ResponseBody, String> { responseBody -> responseBody.toString() }
20+
21+
@Test
22+
fun `extract error from HttpException test`() {
23+
val serverResponse = "Server Error".toResponseBody()
24+
val response = Response.error<String>(404, serverResponse)
25+
val exception = HttpException(response)
26+
with(exception.extractFromHttpException<String, String>(errorConverter)) {
27+
28+
shouldBeTypeOf<NetworkResponse.ServerError<String>>()
29+
this as NetworkResponse.ServerError
30+
31+
body shouldBe errorConverter.convert(serverResponse)
32+
code shouldBe 404
33+
headers shouldBe Headers.headersOf()
34+
}
35+
}
36+
37+
@Test
38+
fun `extract error from IOException test`() {
39+
val ioException = IOException()
40+
with(ioException.extractNetworkResponse<String, String>(errorConverter)) {
41+
42+
shouldBeTypeOf<NetworkResponse.NetworkError>()
43+
this as NetworkResponse.NetworkError
44+
45+
error shouldBeSameInstanceAs ioException
46+
}
47+
}
48+
49+
private class CustomException: Exception()
50+
51+
@Test(expected = CustomException::class)
52+
fun `extract error from an unknown exception test`() {
53+
val exception = CustomException()
54+
exception.extractNetworkResponse<String, String>(errorConverter)
55+
}
56+
}

src/test/kotlin/com/haroldadmin/cnradapter/FactoryTest.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@ import io.kotlintest.shouldBe
55
import io.kotlintest.specs.DescribeSpec
66
import kotlinx.coroutines.Deferred
77
import okhttp3.mockwebserver.MockWebServer
8+
import retrofit2.Call
89
import retrofit2.Retrofit
910
import java.util.concurrent.ExecutorService
1011
import java.util.concurrent.Executors
1112

1213
internal class FactoryTest : DescribeSpec() {
1314

1415
private lateinit var mockWebServer: MockWebServer
15-
private lateinit var callAdapterFactory: CoroutinesNetworkResponseAdapterFactory
16+
private lateinit var callAdapterFactory: NetworkResponseAdapterFactory
1617
private lateinit var retrofit: Retrofit
1718
private lateinit var executor: ExecutorService
1819

1920
override fun beforeSpec(spec: Spec) {
2021
super.beforeSpec(spec)
2122
mockWebServer = MockWebServer()
22-
callAdapterFactory = CoroutinesNetworkResponseAdapterFactory()
23+
callAdapterFactory = NetworkResponseAdapterFactory()
2324
executor = Executors.newSingleThreadExecutor()
2425
retrofit = Retrofit.Builder()
2526
.baseUrl(mockWebServer.url("/"))
@@ -45,12 +46,18 @@ internal class FactoryTest : DescribeSpec() {
4546
callAdapterFactory.get(bodyClass, emptyArray(), retrofit) shouldBe null
4647
}
4748
}
48-
context("Request type if Deferred<NetworkResponse>") {
49+
context("Request type is Deferred<NetworkResponse>") {
4950
val bodyClass = typeOf<Deferred<NetworkResponse<String, String>>>()
5051
it("Should return correct type") {
5152
callAdapterFactory.get(bodyClass, emptyArray(), retrofit)!!.responseType() shouldBe String::class.java
5253
}
5354
}
55+
context("Request type is NetworkResponse") {
56+
val bodyClass = typeOf<Call<NetworkResponse<String, String>>>()
57+
it("should return the correct type") {
58+
callAdapterFactory.get(bodyClass, emptyArray(), retrofit)!!.responseType() shouldBe String::class.java
59+
}
60+
}
5461
}
5562
}
5663
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.haroldadmin.cnradapter
2+
3+
import com.haroldadmin.cnradapter.CompletableCall
4+
import com.haroldadmin.cnradapter.NetworkResponse
5+
import com.haroldadmin.cnradapter.NetworkResponseCall
6+
import io.kotlintest.matchers.types.shouldBeTypeOf
7+
import io.kotlintest.matchers.types.shouldNotBeSameInstanceAs
8+
import io.kotlintest.shouldBe
9+
import okhttp3.ResponseBody
10+
import okhttp3.ResponseBody.Companion.toResponseBody
11+
import org.junit.Test
12+
import retrofit2.*
13+
import java.io.IOException
14+
15+
internal class NetworkResponseCallTest {
16+
private val errorConverter = Converter<ResponseBody, String> { it.toString() }
17+
private val backingCall = CompletableCall<String>()
18+
private val networkResponseCall = NetworkResponseCall<String, String>(backingCall, errorConverter)
19+
20+
@Test(expected = UnsupportedOperationException::class)
21+
fun `should throw an error when invoking 'execute'`() {
22+
networkResponseCall.execute()
23+
}
24+
25+
@Test
26+
fun `should delegate properties to backing call`() {
27+
with (networkResponseCall) {
28+
isExecuted shouldBe backingCall.isExecuted
29+
isCanceled shouldBe backingCall.isCanceled
30+
request() shouldBe backingCall.request()
31+
}
32+
}
33+
34+
@Test
35+
fun `should return new instance when cloned`() {
36+
val clonedCall = networkResponseCall.clone()
37+
clonedCall shouldNotBeSameInstanceAs networkResponseCall
38+
}
39+
40+
@Test
41+
fun `should cancel backing call as well when canceled`() {
42+
networkResponseCall.cancel()
43+
assert(backingCall.isCanceled)
44+
}
45+
46+
@Test
47+
fun `should parse successful call as NetworkResponse Success`() {
48+
val body = "Test body"
49+
networkResponseCall.enqueue(object : Callback<NetworkResponse<String, String>> {
50+
override fun onResponse(call: Call<NetworkResponse<String, String>>, response: Response<NetworkResponse<String, String>>) {
51+
assert(response.isSuccessful)
52+
response.body().shouldBeTypeOf<NetworkResponse.Success<String>>()
53+
(response.body()!! as NetworkResponse.Success).body shouldBe body
54+
}
55+
56+
override fun onFailure(call: Call<NetworkResponse<String, String>>, t: Throwable) {
57+
throw IllegalStateException()
58+
}
59+
})
60+
backingCall.complete(body)
61+
}
62+
63+
@Test
64+
fun `should parse unsuccessful call with HttpException as NetworkResponse ServerError`() {
65+
networkResponseCall.enqueue(object : Callback<NetworkResponse<String, String>> {
66+
override fun onResponse(call: Call<NetworkResponse<String, String>>, response: Response<NetworkResponse<String, String>>) {
67+
response.body().shouldBeTypeOf<NetworkResponse.ServerError<String>>()
68+
}
69+
70+
override fun onFailure(call: Call<NetworkResponse<String, String>>, t: Throwable) {
71+
throw IllegalStateException()
72+
}
73+
})
74+
75+
backingCall.completeWithException(HttpException(Response.error<String>(404, "Server Error".toResponseBody())))
76+
}
77+
78+
@Test
79+
fun `should parse unsuccessful call with IOException as NetworkResponse NetworkError`() {
80+
networkResponseCall.enqueue(object: Callback<NetworkResponse<String, String>> {
81+
override fun onFailure(call: Call<NetworkResponse<String, String>>, t: Throwable) {
82+
throw IllegalStateException()
83+
}
84+
85+
override fun onResponse(call: Call<NetworkResponse<String, String>>, response: Response<NetworkResponse<String, String>>) {
86+
response.body().shouldBeTypeOf<NetworkResponse.NetworkError>()
87+
}
88+
})
89+
90+
backingCall.completeWithException(IOException())
91+
}
92+
93+
@Test
94+
fun `should parse successful call with empty body as NetworkResponse ServerError`() {
95+
networkResponseCall.enqueue(object : Callback<NetworkResponse<String, String>> {
96+
override fun onFailure(call: Call<NetworkResponse<String, String>>, t: Throwable) {
97+
throw IllegalStateException()
98+
}
99+
100+
override fun onResponse(call: Call<NetworkResponse<String, String>>, response: Response<NetworkResponse<String, String>>) {
101+
response.body().shouldBeTypeOf<NetworkResponse.ServerError<String>>()
102+
}
103+
})
104+
105+
backingCall.complete(Response.error(404, "".toResponseBody()))
106+
}
107+
}

src/test/kotlin/com/haroldadmin/cnradapter/RetryTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ExtensionsTest : DescribeSpec() {
2525
retrofit = Retrofit.Builder()
2626
.baseUrl(server.url("/"))
2727
.callbackExecutor(executor)
28-
.addCallAdapterFactory(CoroutinesNetworkResponseAdapterFactory())
28+
.addCallAdapterFactory(NetworkResponseAdapterFactory())
2929
.addConverterFactory(StringConverterFactory())
3030
.build()
3131
service = retrofit.create(Service::class.java)
@@ -41,7 +41,8 @@ class ExtensionsTest : DescribeSpec() {
4141
describe("Execute with retry") {
4242

4343
repeat(9) {
44-
server.enqueue(MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AFTER_REQUEST))
44+
val mockResponse = MockResponse()
45+
server.enqueue(mockResponse.apply { mockResponse.socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST })
4546
}
4647

4748
server.enqueue(MockResponse().setBody("Hi!"))

src/test/kotlin/com/haroldadmin/cnradapter/Service.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ import retrofit2.http.GET
66
interface Service {
77
@GET("/")
88
fun getText(): Deferred<NetworkResponse<String, String>>
9+
10+
@GET("/suspend")
11+
suspend fun getTextSuspend(): NetworkResponse<String, String>
912
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,91 @@
11
package com.haroldadmin.cnradapter
22

3+
import io.kotlintest.matchers.types.shouldBeTypeOf
4+
import io.kotlintest.shouldBe
5+
import io.kotlintest.shouldNotBe
6+
import kotlinx.coroutines.runBlocking
7+
import okhttp3.mockwebserver.MockResponse
8+
import okhttp3.mockwebserver.MockWebServer
9+
import okhttp3.mockwebserver.SocketPolicy
10+
import org.junit.After
11+
import org.junit.Before
12+
import org.junit.Test
13+
import retrofit2.Retrofit
14+
import java.util.concurrent.ExecutorService
15+
import java.util.concurrent.Executors
16+
17+
class SuspendTest {
18+
19+
private lateinit var server: MockWebServer
20+
private lateinit var executor: ExecutorService
21+
private lateinit var retrofit: Retrofit
22+
private lateinit var service: Service
23+
24+
@Before
25+
fun setup() {
26+
server = MockWebServer()
27+
executor = Executors.newSingleThreadExecutor()
28+
retrofit = Retrofit.Builder()
29+
.baseUrl(server.url("/suspend/"))
30+
.addConverterFactory(StringConverterFactory())
31+
.addCallAdapterFactory(NetworkResponseAdapterFactory())
32+
.callbackExecutor(executor)
33+
.build()
34+
service = retrofit.create(Service::class.java)
35+
}
36+
37+
@Test
38+
fun `successful response test`() {
39+
val responseBody = "Hi!"
40+
server.enqueue(
41+
MockResponse()
42+
.setBody(responseBody)
43+
.setResponseCode(200)
44+
.setHeader("TEST","test")
45+
)
46+
47+
val response = runBlocking {
48+
service.getTextSuspend()
49+
}
50+
51+
with(response) {
52+
shouldBeTypeOf<NetworkResponse.Success<String>>()
53+
this as NetworkResponse.Success
54+
body shouldBe responseBody
55+
headers shouldNotBe null
56+
headers!!["TEST"] shouldBe "test"
57+
}
58+
}
59+
60+
@Test
61+
fun `empty response test`() {
62+
val responseCode = 404
63+
server.enqueue(
64+
MockResponse()
65+
.setResponseCode(responseCode)
66+
.setHeader("TEST", "test")
67+
)
68+
val response = runBlocking { service.getTextSuspend() }
69+
70+
with (response) {
71+
shouldBeTypeOf<NetworkResponse.ServerError<String>>()
72+
this as NetworkResponse.ServerError
73+
code shouldBe responseCode
74+
body shouldBe null
75+
headers!!["TEST"] shouldBe "test"
76+
}
77+
}
78+
79+
@Test
80+
fun `network error test`() {
81+
server.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST })
82+
val response = runBlocking { service.getTextSuspend() }
83+
response.shouldBeTypeOf<NetworkResponse.NetworkError>()
84+
}
85+
86+
@After
87+
fun cleanup() {
88+
server.close()
89+
executor.shutdown()
90+
}
91+
}

0 commit comments

Comments
 (0)