Skip to content

Commit

Permalink
Merge pull request #111 from snuhcs-course/test/resetpassword-viewmodel
Browse files Browse the repository at this point in the history
Test/resetpassword viewmodel
  • Loading branch information
JH747 authored Dec 9, 2023
2 parents 06136fd + 0156c48 commit 96da305
Showing 1 changed file with 202 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package com.example.speechbuddy.viewmodel

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.example.speechbuddy.R
import com.example.speechbuddy.data.remote.requests.AuthResetPasswordRequest
import com.example.speechbuddy.domain.SessionManager
import com.example.speechbuddy.repository.AuthRepository
import com.example.speechbuddy.ui.models.ResetPasswordErrorType
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import okhttp3.ResponseBody.Companion.toResponseBody
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import retrofit2.Response

class ResetPasswordViewModelTest {
@OptIn(DelicateCoroutinesApi::class)
private val mainThreadSurrogate = newSingleThreadContext("UI thread")

@MockK
private val repository: AuthRepository = mockk()
private val sessionManager: SessionManager = mockk()
private lateinit var viewModel: ResetPasswordViewModel

private val validPassword = "123456789" //length >= 8
private val invalidPassword = "1234567" //length < 8
private val emptyPassword = ""
private val passwordList = arrayListOf(validPassword, invalidPassword, emptyPassword)
private val invalidPasswordList = arrayListOf(invalidPassword, emptyPassword)

@get:Rule
val instantExecutorRule = InstantTaskExecutorRule()

@OptIn(ExperimentalCoroutinesApi::class)
@Before
fun setup() {
Dispatchers.setMain(mainThreadSurrogate)
viewModel = ResetPasswordViewModel(repository, sessionManager)
}

@OptIn(ExperimentalCoroutinesApi::class)
@After
fun tearDown() {
Dispatchers.resetMain()
mainThreadSurrogate.close()
}

@Test
fun `should return no error when password is empty before click`() {
passwordList.forEach {
viewModel.setPassword(emptyPassword)
viewModel.setPasswordCheck(it)
assertEquals(viewModel.passwordInput, emptyPassword)
assertEquals(viewModel.passwordCheckInput, it)
assertEquals(viewModel.uiState.value.error?.type, null)
assertEquals(viewModel.uiState.value.isValidPassword, false)
}
}

@Test
fun `should return no error when password is valid before click`() {
passwordList.forEach {
viewModel.setPassword(validPassword)
viewModel.setPasswordCheck(it)
assertEquals(viewModel.passwordInput, validPassword)
assertEquals(viewModel.passwordCheckInput, it)
assertEquals(viewModel.uiState.value.error?.type, null)
assertEquals(viewModel.uiState.value.isValidPassword, false)
}
}

@Test
fun `should return no error when password is invalid before click`() {
passwordList.forEach {
viewModel.setPassword(invalidPassword)
viewModel.setPasswordCheck(it)
assertEquals(viewModel.passwordInput, invalidPassword)
assertEquals(viewModel.passwordCheckInput, it)
assertEquals(viewModel.uiState.value.error?.type, null)
assertEquals(viewModel.uiState.value.isValidPassword, false)
}
}

@Test
fun `should set error type PASSWORD when password is empty`() {
val onSuccess: () -> Unit = mockk(relaxed = true)
passwordList.forEach {
viewModel.setPassword(emptyPassword)
viewModel.setPasswordCheck(it)
viewModel.resetPassword(onSuccess)

assertEquals(viewModel.passwordInput, emptyPassword)
assertEquals(viewModel.passwordCheckInput, it)
assertEquals(viewModel.uiState.value.error?.type, ResetPasswordErrorType.PASSWORD)
assertEquals(viewModel.uiState.value.error?.messageId, R.string.no_password)
assertEquals(viewModel.uiState.value.isValidPassword, false)

viewModel.setPassword(validPassword)
assertEquals(viewModel.uiState.value.error?.type, null)
assertEquals(viewModel.uiState.value.isValidPassword, true)
}
} //covers both cases when password equals password check, or not

@Test
fun `should set error type PASSWORD when password is invalid`() {
val onSuccess: () -> Unit = mockk(relaxed = true)
passwordList.forEach {
viewModel.setPassword(invalidPassword)
viewModel.setPasswordCheck(it)
viewModel.resetPassword(onSuccess)

assertEquals(viewModel.passwordInput, invalidPassword)
assertEquals(viewModel.passwordCheckInput, it)
assertEquals(viewModel.uiState.value.error?.type, ResetPasswordErrorType.PASSWORD)
assertEquals(viewModel.uiState.value.error?.messageId, R.string.password_too_short)
assertEquals(viewModel.uiState.value.isValidPassword, false)

viewModel.setPassword(validPassword)
assertEquals(viewModel.uiState.value.error?.type, null)
assertEquals(viewModel.uiState.value.isValidPassword, true)
//checks whether validatePassword works well when changing the password into a valid one
}
} //covers both cases when password equals password check, or not

@Test
fun `should set error type PASSWORD_CHECK when password is valid`() {
val onSuccess: () -> Unit = mockk(relaxed = true)
invalidPasswordList.forEach {
viewModel.setPassword(validPassword)
viewModel.setPasswordCheck(it)
viewModel.resetPassword(onSuccess)

assertEquals(viewModel.passwordInput, validPassword)
assertEquals(viewModel.passwordCheckInput, it)
assertEquals(viewModel.uiState.value.error?.type, ResetPasswordErrorType.PASSWORD_CHECK)
assertEquals(viewModel.uiState.value.error?.messageId, R.string.wrong_password_check)
assertEquals(viewModel.uiState.value.isValidPassword, false)
//isValidPassword doesn't turn true when password was valid from the start
}
}

@Test
fun `should success when password is valid and equals password check`() {
val onSuccess: () -> Unit = mockk(relaxed = true)
viewModel.setPassword(validPassword)
viewModel.setPasswordCheck(validPassword)
coEvery {
repository.resetPassword(AuthResetPasswordRequest(validPassword))
} returns flowOf(Response.success(null))
viewModel.resetPassword(onSuccess)
Thread.sleep(10)

assertEquals(viewModel.uiState.value.error?.type, null)
coVerify { sessionManager.deleteToken() }
}

@Test
fun `should fail when bad request occurs`() {
val onSuccess: () -> Unit = mockk(relaxed = true)
val errorResponseBody = "Error message or JSON here".toResponseBody(null)
val errorResponse: Response<Void> = Response.error(400, errorResponseBody)
viewModel.setPassword(validPassword)
viewModel.setPasswordCheck(validPassword)
coEvery {
repository.resetPassword(AuthResetPasswordRequest(validPassword))
} returns flowOf(errorResponse)
viewModel.resetPassword(onSuccess)
Thread.sleep(10)

assertEquals(viewModel.uiState.value.error?.type, ResetPasswordErrorType.PASSWORD)
assertEquals(viewModel.uiState.value.error?.messageId, R.string.unknown_error)
}

@Test
fun `should fail when no internet connection`() {
val onSuccess: () -> Unit = mockk(relaxed = true)
val errorResponseBody = "Error message or JSON here".toResponseBody(null)
val errorResponse: Response<Void> = Response.error(600, errorResponseBody)
viewModel.setPassword(validPassword)
viewModel.setPasswordCheck(validPassword)
coEvery {
repository.resetPassword(AuthResetPasswordRequest(validPassword))
} returns flowOf(errorResponse)
viewModel.resetPassword(onSuccess)
Thread.sleep(10)

assertEquals(viewModel.uiState.value.error?.type, ResetPasswordErrorType.CONNECTION)
assertEquals(viewModel.uiState.value.error?.messageId, R.string.connection_error)
}
}

0 comments on commit 96da305

Please sign in to comment.