Skip to content

Commit

Permalink
Add ApiService integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonShapovalov committed Dec 12, 2024
1 parent 969e7d3 commit 466f081
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 14 deletions.
5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,15 @@ dependencies {
implementation(libs.moshi.kotlin)
ksp(libs.moshi.kotlin.codegen)

// Unit testing
// Unit/Integration testing
testImplementation(libs.kotlin.test)
testImplementation(libs.junit)
testImplementation(libs.mockk)
testImplementation(libs.mockk.android)
testImplementation(libs.mockk.agent)
testImplementation(libs.robolectric)
testImplementation(libs.mock.web.server)
testImplementation(libs.hilt.android.testing)
testImplementation(libs.kotlinx.coroutines.test)

// Instrumentation testing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import kotlin.test.assertTrue

/**
* Instrumented test, which will execute on an Android device.
* This test should not be a part of CI/CD pipeline.
* Rather it should be executed manually to check API key is valid.
* To check Hilt and Retrofit setup, see "ApiServiceIntegrationTest".
*
* See [testing documentation](http://d.android.com/tools/testing) and
* [hilt](https://developer.android.com/training/dependency-injection/hilt-testing#end-to-end).
Expand Down
11 changes: 7 additions & 4 deletions app/src/main/java/clean/architecture/omdb/di/api/ApiModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ object ApiModule {
*/
@Provides
@Singleton
fun provideApiService(): ApiService {
fun provideApiService(moshi: Moshi): ApiService {
val client = buildClient()
val moshi = buildMoshi()
val retrofit = buildRetrofit(client, moshi)
return retrofit.create(ApiService::class.java)
}

/**
* Factory method to provide instance of [Moshi].
*/
@Provides
fun provideMoshi(): Moshi = Moshi.Builder().build()

private fun buildRetrofit(
client: OkHttpClient,
moshi: Moshi
Expand All @@ -53,6 +58,4 @@ object ApiModule {
HttpLoggingInterceptor.Level.NONE
}
}

private fun buildMoshi(): Moshi = Moshi.Builder().build()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package clean.architecture.omdb.data.api

import clean.architecture.data.api.ApiService
import clean.architecture.data.api.config.ApiConfig
import clean.architecture.data.api.model.SearchResponse
import clean.architecture.data.api.model.SearchResponse.Movie
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
import kotlinx.coroutines.test.runTest
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.Before
import org.junit.Rule
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import javax.inject.Inject
import kotlin.test.Test
import kotlin.test.assertEquals

@RunWith(RobolectricTestRunner::class)
@Config(application = HiltTestApplication::class)
@HiltAndroidTest
class ApiServiceIntegrationTest {

@get:Rule
val mockWebServer = MockWebServer()

@get:Rule
val hiltRule = HiltAndroidRule(this)

@Inject
lateinit var apiService: ApiService

@Inject
lateinit var moshi: Moshi

private lateinit var jsonAdapter: JsonAdapter<SearchResponse>

@Before
fun init() {
ApiConfig.setBaseUrl(mockWebServer.url("/").toString())
hiltRule.inject()
jsonAdapter = moshi.adapter(SearchResponse::class.java)
}

@Test
fun `when searching movies, given search response, then return list of movies`() = runTest {
// Given
val movie = _movie.copy(title = "test")
val search = _searchResponse.copy(movies = listOf(movie))
val json = jsonAdapter.toJson(search)
val mockResponse = MockResponse()
.setBody(json)
.setResponseCode(200)
.addHeader("Content-Type", "application/json")
mockWebServer.enqueue(mockResponse)

// When
val result = apiService.search(apiKey = "testKey", title = "test")

// Then
assertEquals(listOf(movie), result.movies)
}

private val _movie = Movie(
title = "",
year = "",
imdbID = "",
type = "",
poster = ""
)

private val _searchResponse = SearchResponse(
movies = emptyList(),
totalResults = "",
response = ""
)
}
21 changes: 12 additions & 9 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
[versions]
agp = "8.7.2"
agp = "8.7.3"
kotlin = "2.0.20"
ksp = "2.0.20-1.0.24"
coreKtx = "1.13.1"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.6"
activityCompose = "1.9.2"
navigationCompose = "2.8.2"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
navigationCompose = "2.8.5"
hiltNavigationCompose = "1.2.0"
composeBom = "2024.09.03"
composeBom = "2024.12.01"
retrofit = "2.11.0"
loggingInterceptor = "4.12.0"
okhttp = "4.12.0"
moshi = "1.15.1"
detekt = "1.23.6"
detekt = "1.23.7"
hilt = "2.52"
secretsGradlePlugin = "2.0.1"
mockk = "1.13.12"
robolectric = "4.14"
kotlinxCoroutinesCore = "1.9.0"
kover = "0.8.3"
room = "2.6.1"
Expand All @@ -44,7 +45,8 @@ androidx-room-compiler = { group = "androidx.room", name = "room-compiler", vers
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-converter-moshi = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "retrofit" }
logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "loggingInterceptor" }
logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
mock-web-server = { group = "com.squareup.okhttp3", name = "mockwebserver", version.ref = "okhttp" }
moshi-kotlin = { group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "moshi" }
moshi-kotlin-codegen = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "moshi" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
Expand All @@ -54,6 +56,7 @@ secrets-gradle-plugin = { group = "com.google.android.libraries.mapsplatform.sec
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
mockk-android = { group = "io.mockk", name = "mockk-android", version.ref = "mockk" }
mockk-agent = { group = "io.mockk", name = "mockk-agent", version.ref = "mockk" }
robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesCore" }
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" }
Expand Down

0 comments on commit 466f081

Please sign in to comment.