StateFlow: модульное тестирование StateFlow в Kotlin Multiplaform - PullRequest
1 голос
/ 14 июля 2020

Кто-нибудь пробовал StateFlow в KMP? Если да, может ли кто-нибудь посоветовать мне написать модульный тест для StateFlow? Я следую архитектуре MVVM и пытаюсь создать общую модель просмотра, которая может использоваться на любой платформе.

В приведенном ниже примере я пытаюсь протестировать LoginViewModel из его метода входа .

LoginViewModel.kt:

@ExperimentalCoroutinesApi
class LoginViewModel(private val loginViewState: LoginViewState, private val loginService: LoginService) {
    private val _apiStateFlow = MutableStateFlow(loginViewState)
    val stateFlow: StateFlow<LoginViewState> = _apiStateFlow
    private val _EMAIL_REGEX = "^[A-Za-z](.*)([@]{1})(.{1,})(\\.)(.{1,})"
    suspend fun login(email: String, password: String) {
        if (isFormValid(email, password)) {
            _apiStateFlow.value = loginViewState.copy(isLoginApiLoading = true)
            runCatching {
                loginService.login(email, password)
            }.onSuccess {
                loginViewState.copy(isLoginApiLoading = false, loginResponse = it)
            }.onFailure {
                loginViewState.copy(isLoginApiLoading = false, errorResponse = ErrorResponse(it.message.orEmpty()))
            }
        }
    }
    fun isFormValid(email: String, password: String): Boolean {
        val isEmailValid = email.isEmailValid()
        val isPasswordValid = password.isPasswordValid()
        _apiStateFlow.value = loginViewState.copy(isValidEmail = isEmailValid, isValidPassword = isPasswordValid)
        return isEmailValid && isPasswordValid
    }
    private fun String.isEmailValid() = _EMAIL_REGEX.toRegex().matches(this)
    private fun String.isPasswordValid() = length in 6..14
}
data class LoginViewState(
    val email: String = "",
    val password: String = "",
    val isValidEmail: Boolean = false,
    val isValidPassword: Boolean = false,
    val isLoginApiLoading: Boolean = false,
    val loginResponse: LoginResponse? = null,
    val errorResponse: ErrorResponse? = null
)

Service.kt:

expect val platformEngine: HttpClientEngineFactory<HttpClientEngineConfig>
val myHttpClient: HttpClient
get() = HttpClient(platformEngine) {
    install(JsonFeature) {
        serializer = KotlinxSerializer()
    }
    install(Logging) {
        level = LogLevel.ALL
    }
    expectSuccess = false
    HttpResponseValidator {
        validateResponse { response: HttpResponse ->
            println("Response: $response")
            val statusCode = response.status.value
            when (statusCode) {
                in 300..399 -> throw RedirectResponseException(response)
                in 400..499 -> throw ClientRequestException(response)
                in 500..599 -> throw ServerResponseException(response)
            }
            if (statusCode >= 600) {
                throw ResponseException(response)
            }
        }
        handleResponseException { cause: Throwable ->
            println("Exception: $cause")
        }
    }
    defaultRequest {
        url {
            host = "127.0.0.1:8080/"
            protocol = URLProtocol.HTTP
        }
        timeout {
            connectTimeoutMillis = 10000
            requestTimeoutMillis = 10000
            socketTimeoutMillis = 10000
        }
    }
}

LoginService.kt:

class LoginService(private val httpClient: HttpClient) {
    suspend fun login(email : String, password : String): LoginResponse = httpClient.post {
        body = LoginRequest(email, password)
    }
}

LoginReqRes.kt:

data class LoginRequest(val email: String = "", val password: String = "")
data class LoginResponse(val name: String = "", val age: Int = 0, val email: String = "")
data class ErrorResponse(val message: String)
...