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

Test/android post #83

Merged
merged 4 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions android/app/src/main/java/com/goliath/emojihub/models/Post.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@ class Post(
val createdBy: String = dto.createdBy
val content: String = dto.content
val reaction: List<String> = dto.reaction

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Post
if (id != other.id) return false
if (createdAt != other.createdAt) return false
if (modifiedAt != other.modifiedAt) return false
if (createdBy != other.createdBy) return false
if (content != other.content) return false
if (reaction != other.reaction) return false

return true
}

override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + createdAt.hashCode()
result = 31 * result + modifiedAt.hashCode()
result = 31 * result + createdBy.hashCode()
result = 31 * result + content.hashCode()
result = 31 * result + reaction.hashCode()
return result
}
}

data class PostDto(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton

sealed interface PostUseCase {
interface PostUseCase {
val postList: StateFlow<PagingData<Post>>
val myPostList: StateFlow<PagingData<Post>>
suspend fun updatePostList(data: PagingData<Post>)
Expand All @@ -26,6 +27,7 @@ sealed interface PostUseCase {
suspend fun deletePost(id: String)
}

@Singleton
class PostUseCaseImpl @Inject constructor(
private val repository: PostRepository,
private val errorController: ApiErrorController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.update
import javax.inject.Inject
import javax.inject.Singleton

sealed interface UserUseCase {
interface UserUseCase {
val userState: StateFlow<User?>

suspend fun fetchUserList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.goliath.emojihub.repositories.remote
import androidx.paging.testing.asSnapshot
import com.goliath.emojihub.data_sources.api.PostApi
import com.goliath.emojihub.mockLogClass
import com.goliath.emojihub.models.PostDto
import com.goliath.emojihub.models.UploadPostDto
import com.goliath.emojihub.samplePostDto
import io.mockk.coEvery
import io.mockk.coVerify
import retrofit2.Response
Expand All @@ -20,16 +20,6 @@ import org.junit.runners.JUnit4
class PostRepositoryImplTest {
private val postApi = mockk<PostApi>()
private val postRepositoryImpl = PostRepositoryImpl(postApi)
private val samplePostDto = PostDto(
id = "1234",
createdAt = "2023.09.16",
createdBy = "channn",
content = "조금 전에 앞에 계신 분이 실수로 지갑을 흘리셨다. " +
"지갑이 하수구 구멍으로 빠지려는 찰나, 발로 굴러가는 지갑을 막아서 다행히 참사는 막을 수 있었다. " +
"지갑 주인분께서 감사하다고 카페 드림에서 커피도 한 잔 사주셨다.",
modifiedAt = "2023.10.23",
reaction = listOf("good", "check", "good")
)
@Before
fun setUp() {
mockLogClass()
Expand All @@ -56,6 +46,27 @@ class PostRepositoryImplTest {
}
}

@Test
fun fetchMyPostList_returnsFlowOfPagingDataOfPostDto() {
// given
val numSamplePosts = 10
val samplePostDtoList = List(numSamplePosts) { samplePostDto }
val expectedFetchedPostDtoList = List(numSamplePosts*2) { samplePostDto }
// *2 because of .asSnapshot() load one more time
coEvery {
postApi.fetchMyPostList(any())
} returns Response.success(samplePostDtoList)
// when
val fetchedPostPagingDataFlow = runBlocking { postRepositoryImpl.fetchMyPostList() }
val fetchedPostDtoList = runBlocking { fetchedPostPagingDataFlow.asSnapshot() }
// then
coVerify(exactly = 2) { postApi.fetchMyPostList(any()) }
runBlocking {
assertEquals(expectedFetchedPostDtoList.size, fetchedPostDtoList.size)
assertEquals(expectedFetchedPostDtoList, fetchedPostDtoList)
}
}

@Test
fun uploadPost_returnsSuccessResponse() {
// given
Expand Down Expand Up @@ -118,7 +129,7 @@ class PostRepositoryImplTest {
postApi.editPost(any(), any())
} returns Response.success(Unit)
// when
val response = runBlocking {
runBlocking {
postRepositoryImpl.editPost(samplePostDto.id, samplePostDto.content)
}
// then
Expand All @@ -137,7 +148,7 @@ class PostRepositoryImplTest {
postApi.deletePost(any())
} returns Response.success(Unit)
// when
val response = runBlocking { postRepositoryImpl.deletePost(samplePostDto.id) }
runBlocking { postRepositoryImpl.deletePost(samplePostDto.id) }
// then
coVerify(exactly = 1) { postApi.deletePost(samplePostDto.id) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ package com.goliath.emojihub.usecases
import androidx.paging.PagingData
import androidx.paging.map
import androidx.paging.testing.asSnapshot
import com.goliath.emojihub.createDeterministicDummyPostDtoList
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 com.goliath.emojihub.samplePostDto
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import org.junit.Assert.*
Expand All @@ -39,10 +39,20 @@ class PostUseCaseImplTest {
assertEquals(samplePagingPostData, postUseCaseImpl.postList.value)
}

@Test
fun updateMyPostList_withSamplePagingPostData_updatesMyPostListStateFlow() {
// given
val samplePagingPostData = mockk<PagingData<Post>>()
// when
runBlocking { postUseCaseImpl.updateMyPostList(samplePagingPostData) }
// then
assertEquals(samplePagingPostData, postUseCaseImpl.myPostList.value)
}

@Test
fun fetchPostList_returnsFlowOfPostPagingData() {
// given
val samplePostPagingDataFlow = spyk<Flow<PagingData<PostDto>>>()
val samplePostPagingDataFlow = createDeterministicDummyPostDtoList(5)
val sampleAnswer = samplePostPagingDataFlow.map { it.map { dto -> Post(dto) } }
coEvery {
postRepository.fetchPostList()
Expand All @@ -52,10 +62,38 @@ class PostUseCaseImplTest {
// then
coVerify(exactly = 1) { postRepository.fetchPostList() }
runBlocking {
assertEquals(
sampleAnswer.asSnapshot(),
fetchedPostPagingDataFlow.asSnapshot()
)
val sampleAnswerAsSnapshot = sampleAnswer.asSnapshot()
val fetchedPostPagingDataFlowAsSnapshot = fetchedPostPagingDataFlow.asSnapshot()
for (i in sampleAnswerAsSnapshot.indices) {
assertEquals(
sampleAnswerAsSnapshot[i],
fetchedPostPagingDataFlowAsSnapshot[i]
)
}
}
}

@Test
fun fetchMyPostList_returnsFlowOfPostPagingData() {
// given
val samplePostPagingDataFlow = createDeterministicDummyPostDtoList(5)
val sampleAnswer = samplePostPagingDataFlow.map { it.map { dto -> Post(dto) } }
coEvery {
postRepository.fetchMyPostList()
} returns samplePostPagingDataFlow
// when
val fetchedPostPagingDataFlow = runBlocking { postUseCaseImpl.fetchMyPostList() }
// then
coVerify(exactly = 1) { postRepository.fetchMyPostList() }
runBlocking {
val sampleAnswerAsSnapshot = sampleAnswer.asSnapshot()
val fetchedPostPagingDataFlowAsSnapshot = fetchedPostPagingDataFlow.asSnapshot()
for (i in sampleAnswerAsSnapshot.indices) {
assertEquals(
sampleAnswerAsSnapshot[i],
fetchedPostPagingDataFlowAsSnapshot[i]
)
}
}
}

Expand Down Expand Up @@ -93,14 +131,6 @@ class PostUseCaseImplTest {
fun getPostWithId_success_returnsPostDto() {
// given
val sampleId = "sampleId"
val samplePostDto = PostDto(
sampleId,
"sampleCreatedAt",
"sampleUpdatedAt",
"sampleUserId",
"sampleUserAvatarUrl",
listOf("sampleReaction1", "sampleReaction2"),
)
coEvery {
postRepository.getPostWithId(any())
} returns samplePostDto
Expand Down
37 changes: 36 additions & 1 deletion android/app/src/test/java/com/goliath/emojihub/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import androidx.paging.PagingData
import androidx.paging.map
import com.goliath.emojihub.models.Emoji
import com.goliath.emojihub.models.EmojiDto
import com.goliath.emojihub.models.Post
import com.goliath.emojihub.models.PostDto
import io.mockk.every
import io.mockk.mockkStatic
import kotlinx.coroutines.flow.Flow
Expand All @@ -19,6 +21,7 @@ fun mockLogClass() {
every { Log.e(any(), any()) } returns 0
}

// EMOJI TESTING UTILS
val dummyUsernames = listOf("channn", "doggydog", "meow_0w0", "mpunchmm", "kick_back")
val dummyUnicodes = listOf("U+1F44D", "U+1F600", "U+1F970", "U+1F60E", "U+1F621", "U+1F63A", "U+1F496", "U+1F415")
const val dummyMaxSavedCounts = 2000
Expand All @@ -40,7 +43,39 @@ fun createDeterministicDummyEmojiDtoList(listSize : Int): Flow<PagingData<EmojiD
}
return flowOf(PagingData.from(dummyEmojiList))
}

fun createDeterministicDummyEmojiList(listSize: Int): Flow<PagingData<Emoji>> {
return createDeterministicDummyEmojiDtoList(listSize).map { it.map { dto -> Emoji(dto) } }
}

// POST TESTING UTILS
val samplePostDto = PostDto(
id = "1234",
createdAt = "2023.09.16",
createdBy = "channn",
content = "조금 전에 앞에 계신 분이 실수로 지갑을 흘리셨다. " +
"지갑이 하수구 구멍으로 빠지려는 찰나, 발로 굴러가는 지갑을 막아서 다행히 참사는 막을 수 있었다. " +
"지갑 주인분께서 감사하다고 카페 드림에서 커피도 한 잔 사주셨다.",
modifiedAt = "2023.10.23",
reaction = listOf("good", "check", "good")
)
fun createDeterministicDummyPostDtoList(listSize : Int): Flow<PagingData<PostDto>> {
val dummyPostList = mutableListOf<PostDto>()
for (i in 0 until listSize) {
dummyPostList.add(
PostDto(
id = "1234",
createdAt = "2023.09.16",
createdBy = "channn",
content = "조금 전에 앞에 계신 분이 실수로 지갑을 흘리셨다. " +
"지갑이 하수구 구멍으로 빠지려는 찰나, 발로 굴러가는 지갑을 막아서 다행히 참사는 막을 수 있었다. " +
"지갑 주인분께서 감사하다고 카페 드림에서 커피도 한 잔 사주셨다.",
modifiedAt = "2023.10.23",
reaction = listOf("good", "check", "good")
)
)
}
return flowOf(PagingData.from(dummyPostList))
}
fun createDeterministicDummyPostList(listSize: Int): Flow<PagingData<Post>> {
return createDeterministicDummyPostDtoList(listSize).map { it.map { dto -> Post(dto) } }
}
Loading