Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit Test: Repository & UseCase #76

Merged
merged 10 commits into from
Nov 28, 2023
43 changes: 35 additions & 8 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ android {
vectorDrawables {
useSupportLibrary = true
}
// testFunctionalTest = true

// get properties from `local.properties`
buildConfigField("String", "API_BASE_URL", getProperty("API_BASE_URL"))
Expand All @@ -42,27 +43,47 @@ android {
}
}

buildFeatures {
compose = true
buildConfig = true
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}

kotlinOptions {
jvmTarget = "17"
}

buildFeatures {
compose = true
buildConfig = true
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}

composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
testOptions {
unitTests {
isIncludeAndroidResources = true
isReturnDefaultValues = true
}
animationsDisabled = true
}

packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
sourceSets {
getByName("main") {
resources.srcDirs("src/main/assets")
}
getByName("test") {
resources.srcDirs("src/main/assets")
}
getByName("androidTest") {
resources.srcDirs("src/main/assets")
}
}

Expand All @@ -76,6 +97,7 @@ dependencies {

implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")

// jetpack compose
implementation("androidx.activity:activity-compose:1.8.0")
Expand All @@ -94,7 +116,10 @@ dependencies {

// test tools
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.13.5")
testImplementation("androidx.test.ext:junit:1.1.5")
testImplementation("androidx.paging:paging-testing:3.2.1")
testImplementation ("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
Expand All @@ -105,6 +130,8 @@ dependencies {
// hilt
implementation("com.google.dagger:hilt-android:2.44")
kapt("com.google.dagger:hilt-android-compiler:2.44")
kaptTest("com.google.dagger:hilt-android-compiler:2.44")
kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.44")

// navigation
implementation("androidx.navigation:navigation-compose:2.5.3")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.goliath.emojihub

import android.app.Application
import com.goliath.emojihub.data_sources.LocalStorage
import com.goliath.emojihub.data_sources.SharedLocalStorage
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class EmojiHubApplication: Application() {
companion object {
lateinit var preferences: SharedLocalStorage
lateinit var preferences: LocalStorage
}

override fun onCreate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ import android.content.SharedPreferences
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Singleton

interface LocalStorage {
var accessToken: String?
}

@Singleton
class SharedLocalStorage(
@ApplicationContext private val context: Context
) {
) : LocalStorage {
private val preferences: SharedPreferences =
context.getSharedPreferences("EMOJI_HUB", MODE_PRIVATE)

var accessToken: String?
override var accessToken: String?
get() = preferences.getString("accessToken", "")
set(value) = preferences.edit().putString("accessToken", value).apply()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.goliath.emojihub.repositories.remote
import android.util.Log
import com.goliath.emojihub.data_sources.api.EmojiApi
import com.goliath.emojihub.models.EmojiDto
import com.goliath.emojihub.models.FetchEmojiListDto
import com.goliath.emojihub.models.UploadEmojiDto
import com.google.gson.Gson
import okhttp3.MediaType
Expand Down Expand Up @@ -64,12 +63,14 @@ class EmojiRepositoryImpl @Inject constructor(
}
catch (e: IOException) {
Log.d("EmojiRepository", "IOException")
e.printStackTrace()
false
}
catch (e: HttpException) {
Log.d("EmojiRepository", "HttpException")
e.printStackTrace()
false
}
catch (e: Exception) {
Log.d("EmojiRepository", e.message.toString())
false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import javax.inject.Singleton

interface UserRepository {
suspend fun fetchUserList(): Array<UserDtoList>
fun fetchUser(name: String)
fun fetchUser(id: String)
suspend fun registerUser(dto: RegisterUserDto): Response<LoginResponseDto>
suspend fun login(dto: LoginUserDto): Response<LoginResponseDto>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.paging.PagingData
import androidx.paging.map
import com.goliath.emojihub.data_sources.ApiErrorController
import com.goliath.emojihub.models.Post
import com.goliath.emojihub.models.PostDto
import com.goliath.emojihub.models.UploadPostDto
import com.goliath.emojihub.repositories.remote.PostRepository
import kotlinx.coroutines.flow.Flow
Expand All @@ -17,7 +18,7 @@ sealed interface PostUseCase {
suspend fun updatePostList(data: PagingData<Post>)
suspend fun fetchPostList(): Flow<PagingData<Post>>
suspend fun uploadPost(content: String): Boolean
suspend fun getPostWithId(id: String)
suspend fun getPostWithId(id: String): PostDto?
suspend fun editPost(id: String, content: String)
suspend fun deletePost(id: String)
}
Expand Down Expand Up @@ -49,8 +50,8 @@ class PostUseCaseImpl @Inject constructor(
}
}

override suspend fun getPostWithId(id: String) {
repository.getPostWithId(id)
override suspend fun getPostWithId(id: String): PostDto? {
return repository.getPostWithId(id)
}

override suspend fun editPost(id: String, content: String) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package com.goliath.emojihub.repositories.remote

import com.goliath.emojihub.data_sources.api.EmojiApi
import com.goliath.emojihub.mockLogClass
import com.goliath.emojihub.models.UploadEmojiDto
import retrofit2.Response
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.spyk
import kotlinx.coroutines.runBlocking
import org.junit.Assert.*
import org.junit.Before

import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import retrofit2.HttpException
import java.io.File
import java.io.IOException
import java.lang.Exception

@RunWith(JUnit4::class)
class EmojiRepositoryImplTest {
private val emojiApi = mockk<EmojiApi>()
private val emojiRepositoryImpl = EmojiRepositoryImpl(emojiApi)
@Before
fun setUp() {
mockLogClass()
}

// @Test
fun fetchEmojiList() {
TODO("Implement after applying pagination")
}

// @Test
fun getEmojiWithId() {
TODO("Not yet implemented")
}

@Test
fun uploadEmoji_success_returnsTrue() {
// given
mockkStatic(File::class)
val sampleVideoFile = File("sampleVideoFile")
val sampleUploadEmojiDto = mockk<UploadEmojiDto>()
coEvery {
emojiApi.uploadEmoji(any(), any())
} returns Response.success(Unit)
// when
val isUploaded = runBlocking {
emojiRepositoryImpl.uploadEmoji(sampleVideoFile, sampleUploadEmojiDto)
}
// then
coVerify(exactly = 1) { emojiApi.uploadEmoji(any(), any()) }
assertTrue(isUploaded)
}

@Test
fun uploadEmoji_failureWithIOException_returnsFalse() {
// given
mockkStatic(File::class)
val sampleVideoFile = File("sampleVideoFile")
val sampleUploadEmojiDto = mockk<UploadEmojiDto>()
coEvery {
emojiApi.uploadEmoji(any(), any())
} throws mockk<IOException>()
// when
val isUploaded = runBlocking {
emojiRepositoryImpl.uploadEmoji(sampleVideoFile, sampleUploadEmojiDto)
}
// then
coVerify(exactly = 1) { emojiApi.uploadEmoji(any(), any()) }
assertFalse(isUploaded)
}

@Test
fun uploadEmoji_failureWithHttpException_returnsFalse() {
// given
mockkStatic(File::class)
val sampleVideoFile = File("sampleVideoFile")
val sampleUploadEmojiDto = mockk<UploadEmojiDto>()
coEvery {
emojiApi.uploadEmoji(any(), any())
} throws mockk<HttpException>()
// when
val isUploaded = runBlocking {
emojiRepositoryImpl.uploadEmoji(sampleVideoFile, sampleUploadEmojiDto)
}
// then
coVerify(exactly = 1) { emojiApi.uploadEmoji(any(), any()) }
assertFalse(isUploaded)
}

@Test
fun uploadEmoji_failureWithOtherException_returnsFalse() {
// given
mockkStatic(File::class)
val sampleVideoFile = File("sampleVideoFile")
val sampleUploadEmojiDto = mockk<UploadEmojiDto>()
coEvery {
emojiApi.uploadEmoji(any(), any())
} throws spyk<Exception>()
// when
val isUploaded = runBlocking {
emojiRepositoryImpl.uploadEmoji(sampleVideoFile, sampleUploadEmojiDto)
}
// then
coVerify(exactly = 1) { emojiApi.uploadEmoji(any(), any()) }
assertFalse(isUploaded)
}

@Test
fun saveEmoji_success_returnsSuccessResponseUnit() {
// given
val sampleEmojiId = "1234"
coEvery {
emojiApi.saveEmoji(any())
} returns Response.success(Unit)
// when
val response = runBlocking { emojiRepositoryImpl.saveEmoji(sampleEmojiId) }
// then
coVerify(exactly = 1) { emojiApi.saveEmoji(sampleEmojiId) }
assert(response.isSuccessful)
}

@Test
fun saveEmoji_failure_returnsFailureResponseUnit() {
// given
val sampleEmojiId = "1234"
coEvery {
emojiApi.saveEmoji(any())
} returns Response.error(400, mockk(relaxed=true))
// when
val response = runBlocking { emojiRepositoryImpl.saveEmoji(sampleEmojiId) }
// then
coVerify(exactly = 1) { emojiApi.saveEmoji(sampleEmojiId) }
assertFalse(response.isSuccessful)
}

@Test
fun unSaveEmoji_success_returnsSuccessResponseUnit() {
// given
val sampleEmojiId = "1234"
coEvery {
emojiApi.unSaveEmoji(any())
} returns Response.success(Unit)
// when
val response = runBlocking { emojiRepositoryImpl.unSaveEmoji(sampleEmojiId) }
// then
coVerify(exactly = 1) { emojiApi.unSaveEmoji(sampleEmojiId) }
assert(response.isSuccessful)
}

@Test
fun unSaveEmoji_failure_returnsFailureResponseUnit() {
// given
val sampleEmojiId = "1234"
coEvery {
emojiApi.unSaveEmoji(any())
} returns Response.error(400, mockk(relaxed=true))
// when
val response = runBlocking { emojiRepositoryImpl.unSaveEmoji(sampleEmojiId) }
// then
coVerify(exactly = 1) { emojiApi.unSaveEmoji(sampleEmojiId) }
assertFalse(response.isSuccessful)
}

// @Test
fun deleteEmoji() {
TODO("Not yet implemented")
}
}
Loading
Loading