Skip to content

Commit

Permalink
Made ImageRepositoryImpl easily testable and added unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dalafiarisamuel committed Aug 2, 2024
1 parent 2ee33c9 commit 2940209
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 19 deletions.
6 changes: 4 additions & 2 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@ kotlin {
implementation(libs.nappier.logging)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.material3.window.size.multiplatform)

//test implementations
}
commonTest.dependencies {
implementation(libs.koin.test)
implementation(libs.ktor.mock)
implementation(libs.combine)
implementation(libs.kotlin.test)
implementation(libs.kotlin.coroutine.test)
implementation(libs.junit)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface ImageRepository {
query: String,
page: Int,
loadSize: Int
): UnsplashResponseRemote
): Resource<UnsplashResponseRemote>

suspend fun getPhoto(photoId: String): Resource<UnsplashPhotoRemote>
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ internal class ImageRepositoryImpl(private val api: ApiInterface) : ImageReposit
query: String,
page: Int,
loadSize: Int,
): UnsplashResponseRemote {
return api.searchPhotos(query = query, page = page, perPage = loadSize)
): Resource<UnsplashResponseRemote> {
return resourceHelper { api.searchPhotos(query = query, page = page, perPage = loadSize) }
}

override suspend fun getPhoto(photoId: String): Resource<UnsplashPhotoRemote> {
Expand Down
28 changes: 14 additions & 14 deletions composeApp/src/commonMain/kotlin/data/source/ImagePagingSource.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package data.source
package data.source

import androidx.paging.PagingSource
import androidx.paging.PagingState
import data.mapper.PhotoMapper
import data.model.ui.Photo
import data.repository.ImageRepository
import data.repository.Resource

class ImagePagingSource(
class ImagePagingSource(
private val repository: ImageRepository,
private val photoMapper: PhotoMapper,
private val query: String,
Expand All @@ -23,18 +24,17 @@ import data.repository.ImageRepository
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Photo> {
val position = params.key ?: STARTING_PAGE_INDEX

return try {
val response = repository.getImageSearchResult(query, position, params.loadSize)
val photos = photoMapper.mapToUIList(response.results)

LoadResult.Page(
data = photos,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = if (position >= response.totalPages) null else position + 1
)
} catch (exception: Exception) {
exception.printStackTrace()
LoadResult.Error(exception)
return when (val response =
repository.getImageSearchResult(query, position, params.loadSize)) {
is Resource.Failure -> LoadResult.Error(response.error)
is Resource.Success -> {
val photos = photoMapper.mapToUIList(response.result.results)
LoadResult.Page(
data = photos,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = if (position >= response.result.totalPages) null else position + 1
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package data.repository

import di.mapperModule
import di.networkModule
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.test.runTest
import networking.createApiInterface
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.test.KoinTest
import util.NetworkHelper
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

class ImageRepositoryImplTest : KoinTest {

private lateinit var sut: ImageRepository

@BeforeTest
fun setUp() {
startKoin { modules(networkModule(), mapperModule()) }
}

@Test
fun givenSuccessResponse_whenGetImageSearchResultIsCalled_successStateIsReturned() = runTest {
val ktorfit = NetworkHelper.createKtorfitWithMockResponse(
HttpStatusCode.OK,NetworkHelper.successfulImageListResponse
)

val apiService = ktorfit.createApiInterface()

sut = ImageRepositoryImpl(apiService)
val response = sut.getImageSearchResult("Nigeria", 1, 2)
assertNotNull(response)
assertTrue(response is Resource.Success)
}

@Test
fun givenSuccessResponse_whenGetPhotoIsCalled_successStateIsReturned() = runTest {
val ktorfit = NetworkHelper.createKtorfitWithMockResponse(
HttpStatusCode.OK,NetworkHelper.successfulImageResponse
)

val apiService = ktorfit.createApiInterface()

sut = ImageRepositoryImpl(apiService)
val response = sut.getPhoto("Nigeria")
assertNotNull(response)
assertTrue(response is Resource.Success)
}

@Test
fun givenErrorResponse_whenGetPhotoIsCalled_failureStateIsReturned() = runTest {
val ktorfit = NetworkHelper.createKtorfitWithMockResponse(
HttpStatusCode.Unauthorized,NetworkHelper.authorizationError
)

val apiService = ktorfit.createApiInterface()

sut = ImageRepositoryImpl(apiService)
val response = sut.getPhoto("vzpEjttBI0M")
assertNotNull(response)
assertTrue(response is Resource.Failure)
}

@Test
fun givenErrorResponse_whenGetImageSearchResultIsCalled_failureStateIsReturned() = runTest {
val ktorfit = NetworkHelper.createKtorfitWithMockResponse(
HttpStatusCode.OK,NetworkHelper.authorizationError
)

val apiService = ktorfit.createApiInterface()

sut = ImageRepositoryImpl(apiService)
val response = sut.getImageSearchResult("Nigeria", 1, 2)
assertNotNull(response)
assertTrue(response is Resource.Failure)
}

@AfterTest
fun tearDown() {
stopKoin()
}

}
25 changes: 25 additions & 0 deletions composeApp/src/commonTest/kotlin/di/KoinKtTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package di

import data.mapper.PhotoCreatorMapper
import data.mapper.PhotoMapper
import data.mapper.PhotosUrlsMapper
import kotlinx.serialization.json.Json
import org.koin.dsl.module


fun networkModule() = module {
single {

Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
}
}
}

fun mapperModule() = module {
single { PhotoCreatorMapper() }
single { PhotosUrlsMapper() }
single { PhotoMapper(get(), get()) }
}
Loading

0 comments on commit 2940209

Please sign in to comment.