Häufige Muster

Sie können Ihre Compose-App mit bewährten Ansätzen und Mustern testen.

Isoliert testen

Mit ComposeTestRule können Sie eine Aktivität starten, die ein beliebiges Compose-Element anzeigt: Ihre gesamte Anwendung, einen einzelnen Bildschirm oder ein kleines Element. Außerdem sollten Sie prüfen, ob Ihre Composables korrekt gekapselt sind und unabhängig voneinander funktionieren. Das ermöglicht einfachere und gezieltere UI-Tests.

Das bedeutet nicht, dass Sie nur UI-Unittests erstellen sollten. UI-Tests, die größere Teile der UI abdecken, sind ebenfalls sehr wichtig.

Nachdem Sie Ihre eigenen Inhalte festgelegt haben, können Sie auf die Aktivität und die Ressourcen zugreifen.

Häufig müssen Sie den zu testenden Inhalt mit composeTestRule.setContent festlegen und auf Aktivitätsressourcen zugreifen, um beispielsweise zu prüfen, ob ein angezeigter Text mit einer String-Ressource übereinstimmt. Sie können jedoch setContent nicht für eine Regel aufrufen, die mit createAndroidComposeRule() erstellt wurde, wenn die Aktivität sie bereits aufruft.

Ein gängiges Muster dafür ist, eine AndroidComposeTestRule mit einer leeren Aktivität wie ComponentActivity zu erstellen.

class MyComposeTest {

    @get:Rule
    val composeTestRule = createAndroidComposeRule<ComponentActivity>()

    @Test
    fun myTest() {
        // Start the app
        composeTestRule.setContent {
            MyAppTheme {
                MainScreen(uiState = exampleUiState, /*...*/)
            }
        }
        val continueLabel = composeTestRule.activity.getString(R.string.next)
        composeTestRule.onNodeWithText(continueLabel).performClick()
    }
}

Beachten Sie, dass ComponentActivity der Datei AndroidManifest.xml Ihrer Anwendung hinzugefügt werden muss. Fügen Sie Ihrem Modul die folgende Abhängigkeit hinzu, um diese Funktion zu aktivieren:

debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")

Benutzerdefinierte Semantikattribute

Sie können benutzerdefinierte Semantikattribute erstellen, um Informationen für Tests bereitzustellen. Definieren Sie dazu einen neuen SemanticsPropertyKey und stellen Sie ihn mit dem SemanticsPropertyReceiver zur Verfügung.

// Creates a semantics property of type Long.
val PickedDateKey = SemanticsPropertyKey<Long>("PickedDate")
var SemanticsPropertyReceiver.pickedDate by PickedDateKey

Verwenden Sie diese Property jetzt im semantics-Modifikator:

val datePickerValue by remember { mutableStateOf(0L) }
MyCustomDatePicker(
    modifier = Modifier.semantics { pickedDate = datePickerValue }
)

Verwenden Sie in Tests SemanticsMatcher.expectValue, um den Wert des Attributs zu bestätigen:

composeTestRule
    .onNode(SemanticsMatcher.expectValue(PickedDateKey, 1445378400)) // 2015-10-21
    .assertExists()

Statuswiederherstellung prüfen

Prüfen Sie, ob der Status Ihrer Compose-Elemente korrekt wiederhergestellt wird, wenn die Aktivität oder der Prozess neu erstellt wird. Führen Sie solche Prüfungen mit der Klasse StateRestorationTester durch, ohne sich auf die Neuerstellung von Aktivitäten zu verlassen.

Mit dieser Klasse können Sie die Neuerstellung einer zusammensetzbaren Funktion simulieren. Es ist besonders nützlich, um die Implementierung von rememberSaveable zu prüfen.


class MyStateRestorationTests {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun onRecreation_stateIsRestored() {
        val restorationTester = StateRestorationTester(composeTestRule)

        restorationTester.setContent { MainScreen() }

        // TODO: Run actions that modify the state

        // Trigger a recreation
        restorationTester.emulateSavedInstanceStateRestore()

        // TODO: Verify that state has been correctly restored.
    }
}

Verschiedene Gerätekonfigurationen testen

Android-Apps müssen sich an viele sich ändernde Bedingungen anpassen: Fenstergrößen, Sprachen, Schriftgrößen, dunkle und helle Designs und mehr. Die meisten dieser Bedingungen werden von Werten auf Geräteebene abgeleitet, die vom Nutzer gesteuert und mit der aktuellen Configuration-Instanz bereitgestellt werden. Das Testen verschiedener Konfigurationen direkt in einem Test ist schwierig, da im Test Eigenschaften auf Geräteebene konfiguriert werden müssen.

DeviceConfigurationOverride ist eine reine Test-API, mit der Sie verschiedene Gerätekonfigurationen lokal für die zu testenden @Composable-Inhalte simulieren können.

Das Companion-Objekt von DeviceConfigurationOverride hat die folgenden Erweiterungsfunktionen, die Konfigurationsattribute auf Geräteebene überschreiben:

Wenn Sie eine bestimmte Überschreibung anwenden möchten, binden Sie den zu testenden Inhalt in einen Aufruf der Top-Level-Funktion DeviceConfigurationOverride() ein und übergeben Sie die anzuwendende Überschreibung als Parameter.

Im folgenden Code wird beispielsweise die Überschreibung DeviceConfigurationOverride.ForcedSize() angewendet, um die Dichte lokal zu ändern. Dadurch wird die komponierbare Funktion MyScreen in einem großen Querformatfenster gerendert, auch wenn das Gerät, auf dem der Test ausgeführt wird, diese Fenstergröße nicht direkt unterstützt:

composeTestRule.setContent {
    DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
}

Wenn Sie mehrere Überschreibungen gleichzeitig anwenden möchten, verwenden Sie DeviceConfigurationOverride.then():

composeTestRule.setContent {
    DeviceConfigurationOverride(
        DeviceConfigurationOverride.FontScale(1.5f) then
            DeviceConfigurationOverride.FontWeightAdjustment(200)
    ) {
        Text(text = "text with increased scale and weight")
    }
}

Benutzerdefinierte Fehlerbehandlung in Tests

Beim Debuggen von UI-Tests müssen häufig der Bildschirmstatus und die Kompositionshierarchie geprüft werden, wenn eine Assertion fehlschlägt.

Compose bietet eine native Pipeline zur Fehlerbehandlung, um die Diagnose zu optimieren. Sie können Screenshots und UI-Hierarchien bei Fehlern automatisch erfassen oder benutzerdefinierte Handler registrieren, um Absturztelemetriedaten an externe Tools weiterzuleiten.

Fehlerbehandlung konfigurieren

Wenn Sie die Fehlerbehandlung konfigurieren möchten, übergeben Sie ein TestFailurePolicy-Objekt an ComposeUiTestConfig.

Integrierte Fehlerartefakte erfassen

Wenn Sie Screenshots und UI-Hierarchien bei einem Testfehler erfassen möchten, legen Sie die Attribute screenshotCaptureMode und uiHierarchyCaptureMode in TestFailurePolicy fest.

class MyComposeTest {
    private val customConfig = ComposeUiTestConfig(
        failurePolicy = TestFailurePolicy(
            screenshotCaptureMode = TestFailurePolicy.CaptureMode.Enabled,
            uiHierarchyCaptureMode = TestFailurePolicy.CaptureMode.Enabled
        )
    )

    @Test
    fun myFirstTest() = runComposeUiTest(config = customConfig) {
        // ...
    }
}

Benutzerdefinierten Fehler-Handler verwenden

Wenn Sie ein benutzerdefiniertes Verhalten für fehlgeschlagene Tests definieren möchten, fügen Sie der Liste failureHandlers ein TestFailureHandler hinzu. Jeder Handler empfängt ein FailureContext-Objekt, das den ursprünglichen Fehler und eine Liste der Artefakte enthält, die von den vorherigen Pipeline-Handlern generiert wurden.

class MyComposeTestWithHandler {
    private val customConfig = ComposeUiTestConfig(
        failurePolicy = TestFailurePolicy(
            screenshotCaptureMode = TestFailurePolicy.CaptureMode.Enabled,
            failureHandlers = listOf(
                TestFailureHandler { context ->
                    val screenshot = context.artifacts.firstOrNull {
                        it.type == FailureArtifact.Type.Screenshot
                    }
                    // ...
                }
            )
        )
    )

    @get:Rule
    val rule = createComposeRule(config = customConfig)

    // ...
}

Aufnahmen für eine gesamte Test-Suite konfigurieren

Sie können die Erfassung von Fehlerartefakten für eine gesamte Test-Suite aktivieren, indem Sie Argumente für den Instrumentierungs-Runner in der build.gradle-Datei Ihres Moduls übergeben. So müssen Sie nicht mehr einzelne Testdateien aktualisieren.

// build.gradle.kts
android {
    defaultConfig {
        // ...
        testInstrumentationRunnerArguments["androidx.compose.ui.test.failure.isUiHierarchyCaptureEnabled"] = "true"
        testInstrumentationRunnerArguments["androidx.compose.ui.test.failure.isScreenshotCaptureEnabled"] = "true"
    }
}

Rangfolge der Konfigurationen

Das Framework wertet sowohl die lokalen TestFailurePolicy- als auch die Runner-Argumente auf Suite-Ebene aus:

  • Wenn Sie in Ihrem Test-failurePolicy explizit einen Modus auf CaptureMode.Enabled/Disabled festlegen, wird das Runner-Argument überschrieben.
  • Wenn Sie einen Modus auf CaptureMode.Unspecified festlegen (oder failurePolicy als null belassen), greift das Framework auf das Runner-Argument zurück.

Zusätzliche Ressourcen

  • Apps auf Android testen: Auf der Haupt-Landingpage für Android-Tests finden Sie einen umfassenderen Überblick über die Grundlagen und Techniken des Testens.
  • Grundlagen des Testens: Hier finden Sie weitere Informationen zu den grundlegenden Konzepten für das Testen einer Android-App.
  • Lokale Tests: Einige Tests können lokal auf Ihrer Workstation ausgeführt werden.
  • Instrumentierte Tests: Es empfiehlt sich, auch instrumentierte Tests auszuführen. Das sind Tests, die direkt auf dem Gerät ausgeführt werden.
  • Continuous Integration: Mit Continuous Integration können Sie Ihre Tests in Ihre Bereitstellungspipeline einbinden.
  • Verschiedene Bildschirmgrößen testen: Da Nutzer so viele verschiedene Geräte zur Verfügung haben, sollten Sie verschiedene Bildschirmgrößen testen.
  • Espresso: Obwohl Espresso für ansichtsbasierte UIs gedacht ist, kann es auch für einige Aspekte von Compose-Tests hilfreich sein.