← Назад к вопросам

Как тестировать @Composable функции

2.2 Middle🔥 191 комментариев
#UI и вёрстка#Тестирование

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Тестирование @Composable функций в Jetpack Compose

Тестирование @Composable функций в Jetpack Compose осуществляется с использованием специализированных тестовых API, предоставляемых библиотекой compose-ui-test. Современный подход разделяет тестирование на два основных типа: UI тесты (инструментальные) и unit тесты (модульные) с помощью ComposeTestRule и createComposeRule().

Основные инструменты и зависимости

Для начала необходимо добавить зависимости в build.gradle:

dependencies {
    // Тестирование Compose
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
    debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
    
    // Для unit тестов Compose
    testImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
    
    // Робот-паттерн (рекомендуется)
    androidTestImplementation "androidx.test:runner:1.5.2"
}

Типы тестов Compose

1. Инструментальные тесты (UI тесты)

Используют ComposeTestRule для тестирования в эмуляторе или реальном устройстве:

@RunWith(AndroidJUnit4::class)
class MyComposableTest {
    
    @get:Rule
    val composeTestRule = createComposeRule()
    
    @Test
    fun shouldShowInitialText() {
        // Устанавливаем контент
        composeTestRule.setContent {
            MyComposable(text = "Hello, Compose!")
        }
        
        // Проверяем наличие текста
        composeTestRule
            .onNodeWithText("Hello, Compose!")
            .assertIsDisplayed()
    }
    
    @Test
    fun shouldUpdateTextOnClick() {
        var clicked = false
        composeTestRule.setContent {
            MyButtonComposable(
                onClick = { clicked = true }
            )
        }
        
        // Выполняем действие
        composeTestRule
            .onNodeWithTag("myButton")
            .performClick()
        
        // Проверяем результат
        assertTrue(clicked)
    }
}

2. Модульные тесты (Unit тесты)

Для тестирования логики без запуска эмулятора используем createComposeRule() для unit-тестов:

class MyComposableUnitTest {
    
    @get:Rule
    val composeTestRule = createComposeRule()
    
    @Test
    fun testComposableState() {
        composeTestRule.setContent {
            var count by remember { mutableStateOf(0) }
            CounterComposable(count = count, onIncrement = { count++ })
        }
        
        // Проверяем начальное состояние
        composeTestRule
            .onNodeWithText("Count: 0")
            .assertExists()
        
        // Симулируем пользовательское действие
        composeTestRule
            .onNodeWithText("Increment")
            .performClick()
        
        // Проверяем обновленное состояние
        composeTestRule
            .onNodeWithText("Count: 1")
            .assertIsDisplayed()
    }
}

Ключевые методы тестирования

Поиск элементов в UI:

// По тексту
onNodeWithText("Expected Text")

// По тегу (semantics)
onNodeWithTag("testTag")

// По типу содержимого
onNodeWithContentDescription("contentDesc")

// По селектору
onNode(hasText("Text") and isEnabled())

Проверки (Assertions):

.assertIsDisplayed()      // Элемент видим
.assertDoesNotExist()     // Элемент отсутствует
.assertIsEnabled()        // Элемент активен
.assertIsFocused()        // Элемент в фокусе
.assertTextEquals("text") // Проверка точного текста

Действия (Actions):

.performClick()           // Клик
.performTextInput("text") // Ввод текста
.performScrollTo()        // Прокрутка к элементу
.performGesture { ... }   // Сложные жесты

Тестирование состояний и side effects

Для тестирования ViewModels и состояний рекомендуется использовать TestDispatcher:

@Test
fun testComposableWithViewModel() {
    val testDispatcher = StandardTestDispatcher()
    val viewModel = MyViewModel(testDispatcher)
    
    composeTestRule.setContent {
        MyScreen(viewModel = viewModel)
    }
    
    // Запускаем корутины
    testDispatcher.scheduler.advanceUntilIdle()
    
    // Проверяем результаты
    composeTestRule
        .onNodeWithText("Loaded data")
        .assertIsDisplayed()
}

Паттерн Compose Test Robot

Для улучшения читаемости и поддерживаемости тестов рекомендуется использовать Robot-паттерн:

@Test
fun loginScreen_shouldShowError_onInvalidCredentials() {
    composeTestRule.setContent {
        LoginScreen()
    }
    
    loginScreenRobot(composeTestRule) {
        // Действия
        enterEmail("invalid@email")
        enterPassword("123")
        clickLogin()
        
        // Проверки
        verifyErrorDisplayed()
        verifyLoginButtonDisabled()
    }
}

class LoginScreenRobot(
    private val composeTestRule: ComposeTestRule
) {
    fun enterEmail(email: String) {
        composeTestRule
            .onNodeWithTag("emailField")
            .performTextInput(email)
    }
    
    fun verifyErrorDisplayed() {
        composeTestRule
            .onNodeWithText("Invalid email format")
            .assertIsDisplayed()
    }
}

Лучшие практики тестирования

  1. Используйте семантические теги (Modifier.testTag("identifier")) вместо поиска по текстам, которые могут меняться
  2. Тестируйте поведение, а не реализацию - фокус на том, что видит пользователь
  3. Разделяйте тесты на мелкие сценарии - один тест = один сценарий использования
  4. Используйте правило createAndroidComposeRule() для тестов, требующих активности
  5. Мокируйте зависимости для изоляции тестируемого компонента
  6. Тестируйте разные состояния:
    • Состояния загрузки
    • Состояния ошибок
    • Пустые состояния
    • Разные ориентации экрана (при необходимости)

Тестирование тем и конфигураций

Для тестирования различных конфигураций (тем, размеров экрана) используйте DeviceConfigurationOverride:

@Test
fun testComposableInDarkTheme() {
    composeTestRule.setContent {
        DeviceConfigurationOverride(
            DeviceConfigurationOverride.ForcedSize(DpSize(360.dp, 800.dp))
        ) {
            DeviceConfigurationOverride(
                DeviceConfigurationOverride.UiMode(UiMode.NIGHT_YES)
            ) {
                MyComposable()
            }
        }
    }
}

Тестирование @Composable функций требует понимания реактивной природы Compose и правильной работы с состояниями. Современные инструменты предоставляют мощные возможности для создания надежных тестов, которые помогают поддерживать качество UI-слоя приложения.

Как тестировать @Composable функции | PrepBro