O Android oferece uma variedade de ferramentas e APIs que podem ajudar a criar testes para diferentes tamanhos de tela e janela.
DeviceConfigurationOverride
.O elemento combinável DeviceConfigurationOverride
permite substituir
atributos de configuração para testar vários tamanhos de tela e janela em layouts
do Compose. A substituição ForcedSize
se ajusta a qualquer layout no espaço disponível,
o que permite executar qualquer
teste de interface em qualquer tamanho de tela. Por exemplo, você pode usar um formato de smartphone pequeno
para executar todos os testes de interface, incluindo testes de interface para smartphones grandes, dobráveis e
tablets.
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}
Além disso, você pode usar esse elemento combinável para definir escala de fonte, temas e outras propriedades que podem ser testadas em diferentes tamanhos de janela.
Robolectric
Use o Robolectric (em inglês) para executar testes de interface do Compose ou com base em visualização na JVM localmente. Nenhum dispositivo ou emulador é necessário. Você pode configurar o Robolectric para usar tamanhos de tela específicos, entre outras propriedades úteis.
No exemplo a seguir do Now in Android (link em inglês), o Robolectric está configurado para emular um tamanho de tela de 1.000 x 1.000 dp com uma resolução de 480 dpi:
@RunWith(RobolectricTestRunner::class)
// Configure Robolectric to use a very large screen size that can fit all of the test sizes.
// This allows enough room to render the content under test without clipping or scaling.
@Config(qualifiers = "w1000dp-h1000dp-480dpi")
class NiaAppScreenSizesScreenshotTests { ... }
Também é possível definir os qualificadores no corpo do teste, como feito neste snippet do exemplo do Now in Android (link em inglês):
val (width, height, dpi) = ...
// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")
Observe que RuntimeEnvironment.setQualifiers()
atualiza os recursos do sistema e
do aplicativo com a nova configuração, mas não aciona nenhuma ação
em atividades ativas ou outros componentes.
Leia mais na documentação da Configuração de dispositivo do Robolectric.
Dispositivos gerenciados pelo Gradle
O Plug-in do Android para Gradle em dispositivos gerenciados pelo Gradle (GMD, na sigla em inglês) permite definir as especificações dos emuladores e dispositivos reais em que seus testes instrumentados são executados. Crie especificações para dispositivos com diferentes tamanhos de tela para implementar uma estratégia de testes em que certos testes precisam ser executados em determinados tamanhos de tela. Ao usar o GMD com integração contínua (CI, na sigla em inglês), é possível garantir que os testes apropriados sejam executados quando necessário, provisionando e iniciando emuladores e simplificando a configuração de CI.
android {
testOptions {
managedDevices {
devices {
// Run with ./gradlew nexusOneApi30DebugAndroidTest.
nexusOneApi30(com.android.build.api.dsl.ManagedVirtualDevice) {
device = "Nexus One"
apiLevel = 30
// Use the AOSP ATD image for better emulator performance
systemImageSource = "aosp-atd"
}
// Run with ./gradlew foldApi34DebugAndroidTest.
foldApi34(com.android.build.api.dsl.ManagedVirtualDevice) {
device = "Pixel Fold"
apiLevel = 34
systemImageSource = "aosp-atd"
}
}
}
}
}
Você pode encontrar vários exemplos de GMD no projeto testing-samples.
Firebase Test Lab
Use o Firebase Test Lab (FTL, na sigla em inglês) ou um serviço de farm de dispositivos semelhante para executar os testes em dispositivos reais específicos a que você talvez não tenha acesso, como dispositivos dobráveis ou tablets de tamanhos variados. O Firebase Test Lab é um serviço pago com um nível sem custo financeiro. O FTL também é compatível com a execução de testes em emuladores. Esses serviços melhoram a confiabilidade e a velocidade dos testes instrumentados porque podem provisionar dispositivos e emuladores com antecedência.
Para saber mais sobre o uso de FTL com o GMD, consulte Escalonar seus testes com dispositivos gerenciados pelo Gradle.
Testar a filtragem com o executor de testes
Uma estratégia de teste ideal não precisa verificar a mesma coisa duas vezes. Portanto, a maioria dos testes de interface não precisa ser executado em vários dispositivos. Normalmente, os testes de interface são filtrados executando todos ou a maioria deles em um formato de smartphone e apenas um subconjunto em dispositivos com diferentes tamanhos de tela.
É possível anotar determinados testes para serem executados somente com determinados dispositivos e, em seguida, passar um argumento para AndroidJUnitRunner usando o comando que executa os testes.
Por exemplo, é possível criar anotações diferentes:
annotation class TestExpandedWidth
annotation class TestCompactWidth
E use em diferentes testes:
class MyTestClass {
@Test
@TestExpandedWidth
fun myExample_worksOnTablet() {
...
}
@Test
@TestCompactWidth
fun myExample_worksOnPortraitPhone() {
...
}
}
É possível usar a propriedade android.testInstrumentationRunnerArguments.annotation
ao executar os testes para filtrar testes específicos. Por exemplo, se você estiver
usando dispositivos gerenciados pelo Gradle:
$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
Se você não usa o GMD e gerencia emuladores na CI, primeiro confira se o emulador ou dispositivo correto está pronto e conectado e, em seguida, transmita o parâmetro a um dos comandos do Gradle para executar testes instrumentados:
$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
O Espresso Device (consulte a próxima seção) também pode filtrar testes usando as propriedades do dispositivo.
Dispositivo Espresso
Use o Espresso Device para executar ações em emuladores em testes usando qualquer tipo de teste instrumentado, incluindo testes Espresso, Compose ou UI Automator. Essas ações podem incluir a definição do tamanho da tela ou a alternância de estados ou posições dobráveis. Por exemplo, você pode controlar um emulador dobrável e configurá-lo para o modo de mesa. O dispositivo Espresso também contém regras e anotações JUnit para exigir determinados recursos:
@RunWith(AndroidJUnit4::class)
class OnDeviceTest {
@get:Rule(order=1) val activityScenarioRule = activityScenarioRule<MainActivity>()
@get:Rule(order=2) val screenOrientationRule: ScreenOrientationRule =
ScreenOrientationRule(ScreenOrientation.PORTRAIT)
@Test
fun tabletopMode_playerIsDisplayed() {
// Set the device to tabletop mode.
onDevice().setTabletopMode()
onView(withId(R.id.player)).check(matches(isDisplayed()))
}
}
Observe que o Espresso Device ainda está no estágio Alfa e tem os seguintes requisitos:
- Plug-in do Android para Gradle 8.3 ou mais recente
- Android Emulator 33.1.10 ou mais recente
- Dispositivo virtual Android com o nível 24 da API ou mais recente.
Filtrar testes
O Espresso Device pode ler as propriedades dos dispositivos conectados para permitir que você filtre testes usando anotações. Se os requisitos anotados não forem atendidos, os testes serão ignorados.
Requer anotação deDeviceMode
A anotação RequiresDeviceMode
pode ser usada várias vezes para indicar
um teste que será executado somente se todos os valores de DeviceMode
tiverem suporte
ao dispositivo.
class OnDeviceTest {
...
@Test
@RequiresDeviceMode(TABLETOP)
@RequiresDeviceMode(BOOK)
fun tabletopMode_playerIdDisplayed() {
// Set the device to tabletop mode.
onDevice().setTabletopMode()
onView(withId(R.id.player)).check(matches(isDisplayed()))
}
}
Exige anotação de Display
A anotação RequiresDisplay
permite especificar a largura e a altura da
tela do dispositivo usando classes de tamanho, que definem buckets de dimensão
seguindo as classes de tamanho de janela oficiais.
class OnDeviceTest {
...
@Test
@RequiresDisplay(EXPANDED, COMPACT)
fun myScreen_expandedWidthCompactHeight() {
...
}
}
Redimensionar telas
Use o método setDisplaySize()
para redimensionar as dimensões da tela
durante a execução. Use o método em conjunto com a classe DisplaySizeRule
, que garante que todas as mudanças feitas durante os testes sejam desfeitas antes do
próximo teste.
@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {
@get:Rule(order = 1) val activityScenarioRule = activityScenarioRule<MainActivity>()
// Test rule for restoring device to its starting display size when a test case finishes.
@get:Rule(order = 2) val displaySizeRule: DisplaySizeRule = DisplaySizeRule()
@Test
fun resizeWindow_compact() {
onDevice().setDisplaySize(
widthSizeClass = WidthSizeClass.COMPACT,
heightSizeClass = HeightSizeClass.COMPACT
)
// Verify visual attributes or state restoration.
}
}
Quando você redimensiona uma tela com setDisplaySize()
, não afeta a densidade
do dispositivo. Portanto, se uma dimensão não couber no dispositivo de destino, o teste
falhará com uma UnsupportedDeviceOperationException
. Para evitar que os testes
sejam executados nesse caso, use a anotação RequiresDisplay
para filtrá-los:
@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {
@get:Rule(order = 1) var activityScenarioRule = activityScenarioRule<MainActivity>()
// Test rule for restoring device to its starting display size when a test case finishes.
@get:Rule(order = 2) var displaySizeRule: DisplaySizeRule = DisplaySizeRule()
/**
* Setting the display size to EXPANDED would fail in small devices, so the [RequiresDisplay]
* annotation prevents this test from being run on devices outside the EXPANDED buckets.
*/
@RequiresDisplay(
widthSizeClass = WidthSizeClassEnum.EXPANDED,
heightSizeClass = HeightSizeClassEnum.EXPANDED
)
@Test
fun resizeWindow_expanded() {
onDevice().setDisplaySize(
widthSizeClass = WidthSizeClass.EXPANDED,
heightSizeClass = HeightSizeClass.EXPANDED
)
// Verify visual attributes or state restoration.
}
}
StateRestorationTester
A classe StateRestorationTester
é usada para testar a restauração de estado
de componentes combináveis sem recriar atividades. Isso torna os testes mais rápidos
e confiáveis, já que a recriação de atividades é um processo complexo com vários
mecanismos de sincronização:
@Test
fun compactDevice_selectedEmailEmailRetained_afterConfigChange() {
val stateRestorationTester = StateRestorationTester(composeTestRule)
// Set content through the StateRestorationTester object.
stateRestorationTester.setContent {
MyApp()
}
// Simulate a config change.
stateRestorationTester.emulateSavedInstanceStateRestore()
}
Biblioteca Window Testing
A biblioteca Window Testing contém utilitários para ajudar você a criar testes que dependem ou verificam recursos relacionados ao gerenciamento de janelas, como a incorporação de atividades ou recursos dobráveis. O artefato está disponível no repositório Maven do Google.
Por exemplo, é possível usar a função FoldingFeature()
para gerar uma
FoldingFeature
personalizada que pode ser usada nas visualizações do Compose. No Java,
use a função createFoldingFeature()
.
Em uma visualização do Compose, você pode implementar FoldingFeature
da seguinte maneira:
@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
MyApplicationTheme {
ExampleScreen(
displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
)
}
Além disso, é possível emular recursos de tela em testes de interface usando a
função TestWindowLayoutInfo()
.
O exemplo abaixo simula um FoldingFeature
com uma
articulação vertical HALF_OPENED
no centro da tela e verifica se o
layout é o esperado:
Compose
import androidx.window.layout.FoldingFeature.Orientation.Companion.VERTICAL
import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule
@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {
@get:Rule(order=1)
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@get:Rule(order=2)
val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()
@Test
fun foldedWithHinge_foldableUiDisplayed() {
composeTestRule.setContent {
MediaPlayerScreen()
}
val hinge = FoldingFeature(
activity = composeTestRule.activity,
state = HALF_OPENED,
orientation = VERTICAL,
size = 2
)
val expected = TestWindowLayoutInfo(listOf(hinge))
windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)
composeTestRule.waitForIdle()
// Verify that the folding feature is detected and media controls shown.
composeTestRule.onNodeWithTag("MEDIA_CONTROLS").assertExists()
}
}
Visualizações
import androidx.window.layout.FoldingFeature.Orientation
import androidx.window.layout.FoldingFeature.State
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule
@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {
@get:Rule(order=1)
val activityRule = ActivityScenarioRule(MediaPlayerActivity::class.java)
@get:Rule(order=2)
val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()
@Test
fun foldedWithHinge_foldableUiDisplayed() {
activityRule.scenario.onActivity { activity ->
val feature = FoldingFeature(
activity = activity,
state = State.HALF_OPENED,
orientation = Orientation.VERTICAL)
val expected = TestWindowLayoutInfo(listOf(feature))
windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)
}
// Verify that the folding feature is detected and media controls shown.
onView(withId(R.id.media_controls)).check(matches(isDisplayed()))
}
}
Você pode encontrar mais exemplos no projeto WindowManager.
Outros recursos
Documentação
- Diretrizes de qualidade de apps para telas grandes
- Testar apps no Android
- Como testar o layout do Compose
Exemplos
- Exemplo da WindowManager (link em inglês)
- Exemplos de dispositivos Espresso
- Agora no Android
- Usa testes de captura de tela para verificar diferentes tamanhos de tela
Codelabs