Skip to content

Commit

Permalink
Merge pull request #123 from snuhcs-course/test/reaction-unit-test
Browse files Browse the repository at this point in the history
Test: reaction unit test
  • Loading branch information
dawitfamanu authored Dec 9, 2023
2 parents 50a1177 + 6ffd86e commit 62cf027
Show file tree
Hide file tree
Showing 9 changed files with 394 additions and 30 deletions.
22 changes: 22 additions & 0 deletions android/app/src/main/java/com/goliath/emojihub/models/Reaction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,26 @@ class ReactionWithEmoji(
val emojiId: String = dto.emojiId
val postId: String = dto.postId
val emojiDto: EmojiDto? = dto.emojiDto

override fun equals(other: Any?): Boolean {
if (other == null || other !is ReactionWithEmoji) {
return false
}
return this.id == other.id &&
this.createdAt == other.createdAt &&
this.createdBy == other.createdBy &&
this.emojiId == other.emojiId &&
this.postId == other.postId &&
this.emojiDto == other.emojiDto
}

override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + createdAt.hashCode()
result = 31 * result + createdBy.hashCode()
result = 31 * result + emojiId.hashCode()
result = 31 * result + postId.hashCode()
result = 31 * result + (emojiDto?.hashCode() ?: 0)
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface ReactionRepository {
suspend fun fetchReactionList(postId: String, emojiUnicode: String): Flow<PagingData<ReactionWithEmojiDto>>
suspend fun uploadReaction(postId: String, emojiId: String): Response<Unit>
suspend fun getReactionWithId(id: String)
suspend fun deleteReaction(reactionId: String)
suspend fun deleteReaction(reactionId: String): Response<Unit>
}

@Singleton
Expand All @@ -37,7 +37,7 @@ class ReactionRepositoryImpl @Inject constructor(
TODO()
}

override suspend fun deleteReaction(reactionId: String) {
reactionApi.deleteReaction(reactionId)
override suspend fun deleteReaction(reactionId: String): Response<Unit> {
return reactionApi.deleteReaction(reactionId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface ReactionUseCase {

@Singleton
class ReactionUseCaseImpl @Inject constructor(
private val repository: ReactionRepository,
private val reactionRepository: ReactionRepository,
private val errorController: ApiErrorController
): ReactionUseCase {

Expand All @@ -37,11 +37,11 @@ class ReactionUseCaseImpl @Inject constructor(
}

override suspend fun fetchReactionList(postId: String, emojiUnicode: String): Flow<PagingData<ReactionWithEmoji>> {
return repository.fetchReactionList(postId, emojiUnicode).map { it.map { dto -> ReactionWithEmoji(dto) } }
return reactionRepository.fetchReactionList(postId, emojiUnicode).map { it.map { dto -> ReactionWithEmoji(dto) } }
}

override suspend fun uploadReaction(postId: String, emojiId: String): Boolean {
val response = repository.uploadReaction(postId, emojiId)
val response = reactionRepository.uploadReaction(postId, emojiId)
return if (response.isSuccessful) {
true
} else {
Expand All @@ -51,10 +51,10 @@ class ReactionUseCaseImpl @Inject constructor(
}

override suspend fun getReactionWithId(id: String) {
repository.getReactionWithId(id)
reactionRepository.getReactionWithId(id)
}

override suspend fun deleteReaction(reactionId: String) {
repository.deleteReaction(reactionId)
reactionRepository.deleteReaction(reactionId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ReactionViewModel @Inject constructor(

val reactionList = reactionUseCase.reactionList

suspend fun fetchReactionList(postId: String, emojiUnicode: String) {
fun fetchReactionList(postId: String, emojiUnicode: String) {
viewModelScope.launch {
reactionUseCase.fetchReactionList(postId, emojiUnicode)
.cachedIn(viewModelScope)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import com.goliath.emojihub.data_sources.CustomError
import com.goliath.emojihub.data_sources.api.EmojiApi
import com.goliath.emojihub.data_sources.remote.EmojiDataSource
import com.goliath.emojihub.mockLogClass
import com.goliath.emojihub.models.EmojiDto
import com.goliath.emojihub.models.UploadEmojiDto
import com.goliath.emojihub.sampleEmojiDto
import retrofit2.Response
import io.mockk.coEvery
import io.mockk.coVerify
Expand All @@ -31,16 +31,6 @@ class EmojiRepositoryImplTest {
private val emojiApi = mockk<EmojiApi>()
private val emojiDataSource = mockk<EmojiDataSource>()
private val emojiRepositoryImpl = EmojiRepositoryImpl(emojiApi, emojiDataSource)
private val sampleEmojiDto = EmojiDto(
createdBy = "channn",
createdAt = "2023-11-24 14:25:05",
savedCount = 1600,
videoLink = "https://storage.googleapis.com/emojihub-e2023.appspot.com/uu_2023-11-24%2014%3A25%3A05.mp4?GoogleAccessId=firebase-adminsdk-zynbm@emojihub-e2023.iam.gserviceaccount.com&Expires=1709443506&Signature=I%2BNRJSZ7nYtmrWs%2Fjv4uVAeW8%2BfHGF6GeV0pZRE4Sp5gCFuXLXBTKpgRBl1j2F%2BSSUStSqvBlktHZofZznGHWtsMYHQ99%2Bv7wcenqZweSWSmzse4s9sKAOkykn7pB9EMnFgax4VqGK4U5ey5HNSCKsjyNa5ZqDH8%2BqF%2FcIjQ3huChDMB2Xw1InaHUve0syvW6uz%2BeooDLo2nkGxdtElsDtomq2cAUMgk7nRNIYciYLGJ%2FsrscW7%2FXfD3rn%2BH3EM9z5S9DHKHWiEmh1xf0wpTtDsXom7p14XnZunnnOxpNO5OMFJi2x1kxZBFVc7U88V19eTmasWxdGV5TZipfN2ZMA%3D%3D",
thumbnailLink = "https://storage.googleapis.com/emojihub-e2023.appspot.com/uu_2023-11-24%2014%3A25%3A05.jpeg?GoogleAccessId=firebase-adminsdk-zynbm@emojihub-e2023.iam.gserviceaccount.com&Expires=1709443506&Signature=lZK4otdQOXBVKz3EeOEgpSqAH5QE3U6KuTz8bo5RwYQ463i0cBEx44zVPJO3dIP%2B3%2FdKkBbJy%2BzIBogKAKUl5jLyP9FwInOZChspQOuI8zp%2FKivvEZImPnoG2C1UiiwB03tHYq0tWEhgj76BB4SarWRtZY4xRZhuVvuJg9%2FNV%2B5XZ7%2BGGjLbzfjc5rA45iwWQGPfgQN0%2FKJsdTieNb5%2F6%2B5QHW4pq7QLxYAGqvea5X6VY1JcUjXU0iZ%2FfI16L%2F1cFZAMPDPNPxC2bbllFH6vkOdb3qKuvGm0M3Y99GCLTv%2BAiObbBCs13AgmBO1OngrBV4db4zNnjUZOtB0rPRgyFw%3D%3D",
id = "0ZF0MFHOV7974YTV3SBN",
label = "love it",
unicode = "U+2764 U+FE0F"
)
@Before
fun setUp() {
mockLogClass()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.goliath.emojihub.repositories.remote

import androidx.paging.testing.asSnapshot
import com.goliath.emojihub.data_sources.api.ReactionApi
import com.goliath.emojihub.mockLogClass
import com.goliath.emojihub.sampleReactionWithEmojiDto
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
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.Response

@RunWith(JUnit4::class)
class ReactionRepositoryImplTest {
private val reactionApi = mockk<ReactionApi>()
private val reactionRepository = ReactionRepositoryImpl(reactionApi)
@Before
fun setUp() {
mockLogClass()
}

@Test
fun fetchReactionList_returnsFlowOfPagingDataOfReactionWithEmojiDto() {
val numSampleReactions = 10
val sampleReactionWithEmojiDtoList = List(numSampleReactions) { sampleReactionWithEmojiDto }
val expectedFetchedReactionWithEmojiDtoList = List(numSampleReactions*2) { sampleReactionWithEmojiDto }
// *2 because of .asSnapshot() load one more time
coEvery {
reactionApi.fetchReactionList(any(), any(), any(), any())
} returns Response.success(sampleReactionWithEmojiDtoList)
// when
val fetchedReactionPagingDataFlow = runBlocking {
reactionRepository.fetchReactionList("1234", "U+1F44D")
}
val fetchedReactionWithEmojiDtoList = runBlocking {
fetchedReactionPagingDataFlow.asSnapshot()
}
// then
coVerify(exactly = 2) { reactionApi.fetchReactionList(any(), any(), any(), any()) }
runBlocking {
assertEquals(expectedFetchedReactionWithEmojiDtoList.size, fetchedReactionWithEmojiDtoList.size)
assertEquals(expectedFetchedReactionWithEmojiDtoList, fetchedReactionWithEmojiDtoList)
}
}

@Test
fun uploadReaction_success_returnsSuccessResponse() {
// given
val expectedResponse = Response.success(Unit)
coEvery {
reactionApi.uploadReaction(any(), any())
} returns expectedResponse
// when
val response = runBlocking {
reactionRepository.uploadReaction("1234", "1234")
}
// then
coVerify(exactly = 1) { reactionApi.uploadReaction(any(), any()) }
assertEquals(expectedResponse, response)
}

@Test
fun uploadReaction_failure_returnsFailureResponse() {
// given
val expectedResponse = Response.error<Unit>(400, mockk(relaxed = true))
coEvery {
reactionApi.uploadReaction(any(), any())
} returns expectedResponse
// when
val response = runBlocking {
reactionRepository.uploadReaction("1234", "1234")
}
// then
coVerify(exactly = 1) { reactionApi.uploadReaction(any(), any()) }
assertFalse(response.isSuccessful)
}

// @Test
// TODO: Not implemented yet
fun getReactionWithId() {
}

@Test
fun deleteReaction_success_returnsSuccessResponse() {
// given
val expectedResponse = Response.success(Unit)
coEvery {
reactionApi.deleteReaction(any())
} returns expectedResponse
// when
val response = runBlocking {
reactionRepository.deleteReaction("1234")
}
// then
coVerify(exactly = 1) { reactionApi.deleteReaction(any()) }
assertEquals(expectedResponse, response)
}

@Test
fun deleteReaction_failure_returnsFailureResponse() {
// given
val expectedResponse = Response.error<Unit>(400, mockk(relaxed = true))
coEvery {
reactionApi.deleteReaction(any())
} returns expectedResponse
// when
val response = runBlocking {
reactionRepository.deleteReaction("1234")
}
// then
coVerify(exactly = 1) { reactionApi.deleteReaction(any()) }
assertFalse(response.isSuccessful)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.goliath.emojihub.usecases

import androidx.paging.PagingData
import androidx.paging.map
import androidx.paging.testing.asSnapshot
import com.goliath.emojihub.createReactionWithEmojiDtoList
import com.goliath.emojihub.data_sources.ApiErrorController
import com.goliath.emojihub.mockLogClass
import com.goliath.emojihub.models.ReactionWithEmoji
import com.goliath.emojihub.repositories.remote.ReactionRepository
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import io.mockk.spyk
import kotlinx.coroutines.flow.map
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.Response

@RunWith(JUnit4::class)
class ReactionUseCaseImplTest {
private val reactionRepository = mockk<ReactionRepository>()
private val apiErrorController = spyk<ApiErrorController>()
private val reactionUseCase = ReactionUseCaseImpl(reactionRepository, apiErrorController)
@Before
fun setUp() {
mockLogClass()
}

@Test
fun updateReactionList_withSamplePagingReactionData_updatesReactionListStateFlow() {
// given
val samplePagingReactionData = mockk<PagingData<ReactionWithEmoji>>()
// when
runBlocking { reactionUseCase.updateReactionList(samplePagingReactionData) }
// then
assertEquals(samplePagingReactionData, reactionUseCase.reactionList.value)
}

@Test
fun fetchReactionList_returnsFlowOfReactionPagingData() {
// given
val sampleReactionPagingDataFlow = createReactionWithEmojiDtoList(5)
val sampleAnswer = sampleReactionPagingDataFlow.map { it.map { dto -> ReactionWithEmoji(dto) } }
coEvery {
reactionRepository.fetchReactionList(any(), any())
} returns sampleReactionPagingDataFlow
// when
val result = runBlocking { reactionUseCase.fetchReactionList("1234", "U+1F44D") }
// then
coVerify(exactly = 1) { reactionRepository.fetchReactionList(any(), any()) }
runBlocking {
val sampleAnswerAsSnapshot = sampleAnswer.asSnapshot()
val resultAsSnapshot = result.asSnapshot()
for (i in sampleAnswerAsSnapshot.indices) {
assertEquals(sampleAnswerAsSnapshot[i], resultAsSnapshot[i])
}
}
}

@Test
fun uploadReaction_successWithValidPostId_returnsTrue() {
// given
val samplePostId = "1234"
val sampleEmojiId = "5678"
coEvery {
reactionRepository.uploadReaction(any(), any())
} returns Response.success(Unit)
// when
val result = runBlocking { reactionUseCase.uploadReaction(samplePostId, sampleEmojiId) }
// then
assertTrue(result)
}

@Test
fun uploadReaction_failureWithInvalidPostId_returnsFalse() {
// given
val samplePostId = "-1234"
val sampleEmojiId = "5678"
coEvery {
reactionRepository.uploadReaction(any(), any())
} returns Response.error(404, mockk(relaxed = true))
// when
val result = runBlocking { reactionUseCase.uploadReaction(samplePostId, sampleEmojiId) }
// then
assertFalse(result)
}

// @Test
// TODO: Not implemented yet
fun getReactionWithId() {
}

// @Test
// TODO: Not implemented yet
fun deleteReaction() {
}
}
Loading

0 comments on commit 62cf027

Please sign in to comment.