Прежде всего, важно понять, что Compose Multiplatform — это не «напиши один раз, запусти везде» в абсолютном смысле. Это «напиши UI-логику один раз, адаптируй под платформу». Общий код пишется в общем модуле (commonMain), а затем, при необходимости, используются `expect`/`actual` механизмы для платформенно-специфичной реализации отдельных частей (например, навигации, работы с файловой системой). Для тестировщика это означает, что ядро UI-логики и состояние (State) можно тестировать централизованно, но визуальное отображение и поведение на каждой платформе (iOS, Desktop, Web) нужно проверять отдельно.
Первый и главный выбор: на каком уровне проводить тестирование? Compose Multiplatform предлагает несколько уровней абстракции для тестов:
- **Unit-тестирование бизнес-логики и State.** Это самый простой и стабильный уровень. Поскольку ViewModel или простые классы с состоянием, управляющие UI, обычно находятся в общем модуле и не зависят от платформы, их можно тестировать с помощью стандартных инструментов вроде JUnit 5 и Kotlin Test. Используйте `kotlin.test` и `assert` функции. Мокирование зависимостей осуществляется через интерфейсы.
// commonMain
class LoginViewModel(private val authRepository: AuthRepository) {
var loginState by mutableStateOf(LoginState.Idle)
private set
fun login(email: String, password: String) {
viewModelScope.launch {
loginState = LoginState.Loading
loginState = try {
val success = authRepository.login(email, password)
if(success) LoginState.Success else LoginState.Error("Invalid credentials")
} catch (e: Exception) {
LoginState.Error(e.message ?: "Network error")
}
}
}
}
// commonTest
@Test
fun `login success updates state correctly`() = runTest { // Используем kotlinx-coroutines-test
val mockRepo = mockk()
coEvery { mockRepo.login("test@mail.com", "pass") } returns true
val viewModel = LoginViewModel(mockRepo)
viewModel.login("test@mail.com", "pass")
advanceUntilIdle() // Ждем завершения корутины
assertTrue(viewModel.loginState is LoginState.Success)
}
- **Тестирование Composables (UI-компонентов).** Это более сложный уровень. Compose предоставляет библиотеку `compose-ui-test-junit4` (и `junit5`). Для общего модуля можно писать тесты, которые проверяют поведение Composable-функций в изоляции, используя `composeTestRule`. Однако, важно помнить, что эти тесты будут исполняться на JVM (для общего модуля) и не воспроизводят точное поведение на iOS или в браузере. Они проверяют логику композиции и состояния.
// commonTest
@Test
fun `button is disabled when email is empty`() {
composeTestRule.setContent {
MyLoginScreen(viewModel = ...) // Передаем ViewModel с состоянием
}
composeTestRule.onNodeWithTag("loginButton").assertIsNotEnabled()
}
Для такого тестирования в общем модуле вам потребуется добавить зависимости `org.jetbrains.compose.ui:ui-test-junit4` и `org.jetbrains.compose.ui:ui-test-manifest` в `commonTest`. Запускать такие тесты можно из IDE или через Gradle.
- **Интеграционное и UI-тестирование на реальных платформах.** Вот здесь кроется основная сложность и необходимость выбора. Вам нужно решить, как тестировать финальное приложение на iOS, Desktop и Web.
* **Для iOS:** Это самый сложный фронт. Прямого аналога Espresso для Compose Multiplatform на iOS нет. Возможные стратегии:
- **Использование XCUITest:** Поскольку Compose Multiplatform для iOS рендерит нативные UIView, можно писать UI-тесты на Swift/Objective-C, используя стандартный фреймворк Apple XCUITest. Это требует отдельной кодовой базы и знаний Swift.
- **Сквозное тестирование через общую логику:** Максимально вынести проверки в общий модуль (через тестирование ViewModel и State), а на iOS проводить минимальные smoke-тесты через XCUITest, проверяющие лишь запуск приложения и базовые сценарии.
- **Экспериментальные варианты:** Исследовать возможность использования Kaspresso или других Kotlin-фреймворков, которые могут компилироваться в iOS framework, но это пока территория неизведанного.
* **Для Web:** Compose for Web рендерит в DOM. Можно использовать стандартные инструменты тестирования веб-приложений, такие как Selenium, Playwright или Cypress. Поскольку выходной код — это JavaScript/Wasm, потребуется запускать тесты в браузере. Playwright хорошо поддерживается и имеет API на Kotlin/Java.
Исходя из этого, стратегия выбора для QA-команды может выглядеть так:
- **Приоритет 1:** Покрыть unit-тестами всю бизнес-логику и State в общем модуле. Это даст максимальную отдачу и стабильность.
- **Приоритет 2:** Написать тесты для критически важных Composables в общем модуле, используя `composeTestRule`. Это поможет отловить логические ошибки в UI-поведении.
- **Приоритет 3:** Для каждой целевой платформы создать небольшой набор smoke- и регрессионных UI-тестов, используя нативные инструменты (Espresso для Android, XCUITest для iOS, Playwright для Web, Compose Test для Desktop). Фокус — на проверку специфичных для платформы особенностей и интеграции.
- **Рассмотреть сквозное тестирование:** Для ключевых пользовательских сценариев (например, «пользователь регистрируется и совершает покупку») можно реализовать сквозные тесты, возможно, на уровне API или с использованием инструментов вроде Appium (для мобильных платформ) или Playwright (для Web). Но поддерживать их сложнее.
В итоге, выбор Compose Multiplatform для тестировщиков — это не выбор одного инструмента, а определение многоуровневой стратегии, которая балансирует между усилиями на поддержку и полнотой покрытия. Начинайте с тестирования общего кода, а затем постепенно выстраивайте платформенно-специфичное тестирование, фокусируясь на самых рисковых областях.
Комментарии (9)