feat(creator-channel): FanTalk 글 입력 상태 관리를 추가한다
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.reactivex.rxjava3.android.plugins.RxAndroidPlugins
|
||||
import io.reactivex.rxjava3.core.Scheduler
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.never
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [28], application = Application::class)
|
||||
class CreatorChannelFanTalkWriteViewModelTest {
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private lateinit var repository: CreatorChannelRepository
|
||||
private lateinit var viewModel: CreatorChannelFanTalkWriteViewModel
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setImmediateRxSchedulers()
|
||||
SharedPreferenceManager.resetForTest()
|
||||
SharedPreferenceManager.init(context)
|
||||
SharedPreferenceManager.token = "test-token"
|
||||
repository = org.mockito.kotlin.mock()
|
||||
viewModel = CreatorChannelFanTalkWriteViewModel(repository)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
RxJavaPlugins.reset()
|
||||
RxAndroidPlugins.reset()
|
||||
SharedPreferenceManager.resetForTest()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `초기 입력값은 빈 값이고 보내기 버튼은 비활성화된다`() {
|
||||
assertEquals("", viewModel.contentLiveData.requireValue())
|
||||
assertEquals(false, viewModel.isSendEnabledLiveData.requireValue())
|
||||
assertEquals(0, viewModel.contentLengthLiveData.requireValue())
|
||||
assertEquals(false, viewModel.isLoadingLiveData.requireValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `공백과 줄바꿈만 입력하면 보내기 버튼은 비활성화된다`() {
|
||||
viewModel.onContentChanged(" \n \t")
|
||||
|
||||
assertEquals(" \n \t", viewModel.contentLiveData.requireValue())
|
||||
assertEquals(5, viewModel.contentLengthLiveData.requireValue())
|
||||
assertEquals(false, viewModel.isSendEnabledLiveData.requireValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `trim 결과가 비어 있지 않으면 보내기 버튼은 활성화된다`() {
|
||||
viewModel.onContentChanged(" 응원합니다 ")
|
||||
|
||||
assertEquals(true, viewModel.isSendEnabledLiveData.requireValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `500자를 초과하면 입력값과 글자수는 500자로 제한된다`() {
|
||||
val content = "가".repeat(501)
|
||||
|
||||
viewModel.onContentChanged(content)
|
||||
|
||||
assertEquals("가".repeat(500), viewModel.contentLiveData.requireValue())
|
||||
assertEquals(500, viewModel.contentLengthLiveData.requireValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `비활성 상태에서 submit하면 repository를 호출하지 않는다`() {
|
||||
viewModel.submit(creatorId = 100L)
|
||||
|
||||
verify(repository, never()).writeFanTalk(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `creatorId가 유효하지 않으면 repository를 호출하지 않는다`() {
|
||||
viewModel.onContentChanged("응원합니다")
|
||||
|
||||
viewModel.submit(creatorId = 0L)
|
||||
|
||||
verify(repository, never()).writeFanTalk(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `활성 상태에서 submit하면 trim content와 토큰으로 FanTalk 등록 API를 호출한다`() {
|
||||
whenever(repository.writeFanTalk(100L, "응원합니다", "Bearer test-token"))
|
||||
.thenReturn(Single.just(ApiResponse<Any>(true, null, null)))
|
||||
viewModel.onContentChanged(" 응원합니다 ")
|
||||
|
||||
viewModel.submit(creatorId = 100L)
|
||||
|
||||
verify(repository).writeFanTalk(100L, "응원합니다", "Bearer test-token")
|
||||
assertTrue(viewModel.finishEventLiveData.requireValue() == true)
|
||||
assertEquals(false, viewModel.isLoadingLiveData.requireValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `등록 중에는 중복 submit을 무시한다`() {
|
||||
whenever(repository.writeFanTalk(100L, "응원합니다", "Bearer test-token"))
|
||||
.thenReturn(Single.never())
|
||||
viewModel.onContentChanged("응원합니다")
|
||||
|
||||
viewModel.submit(creatorId = 100L)
|
||||
viewModel.submit(creatorId = 100L)
|
||||
|
||||
verify(repository, times(1)).writeFanTalk(100L, "응원합니다", "Bearer test-token")
|
||||
assertEquals(true, viewModel.isLoadingLiveData.requireValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `등록 실패 시 입력값을 유지하고 서버 message를 우선 toast로 emit한다`() {
|
||||
whenever(repository.writeFanTalk(100L, "응원합니다", "Bearer test-token"))
|
||||
.thenReturn(Single.just(ApiResponse<Any>(false, null, "서버 실패")))
|
||||
viewModel.onContentChanged(" 응원합니다 ")
|
||||
|
||||
viewModel.submit(creatorId = 100L)
|
||||
|
||||
assertEquals(" 응원합니다 ", viewModel.contentLiveData.requireValue())
|
||||
assertEquals("서버 실패", viewModel.toastEventLiveData.requireValue())
|
||||
assertEquals(false, viewModel.isLoadingLiveData.requireValue())
|
||||
assertFalse(viewModel.finishEventLiveData.requireValue() == true)
|
||||
}
|
||||
|
||||
private fun setImmediateRxSchedulers() {
|
||||
val trampoline = { _: Scheduler -> Schedulers.trampoline() }
|
||||
RxJavaPlugins.setIoSchedulerHandler(trampoline)
|
||||
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
|
||||
RxAndroidPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() }
|
||||
}
|
||||
|
||||
private fun <T> LiveData<T>.requireValue(): T? {
|
||||
var value: T? = null
|
||||
val observer = Observer<T> { value = it }
|
||||
observeForever(observer)
|
||||
removeObserver(observer)
|
||||
return value
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user