JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.17k stars 1.11k forks source link

No information about UI testing with compose #4167

Closed gastsail closed 4 months ago

gastsail commented 4 months ago

I have been looking on how to do UI testing with compose, I'm looking for simple UI tests with compose

I have readed this article -> https://github.com/JetBrains/compose-multiplatform/tree/master/tutorials/UI_Testing

but I cannot find a way to add it to my project, I have tried

 sourceSets {

        androidMain.dependencies {
           ...
        }
        commonMain.dependencies {
           ...
        }

        @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
        commonTest.dependencies {
             implementation("org.jetbrains.compose.ui:ui-test-junit4:1.5.11")
        }
    }

But I find this issue

ModuleVersionNotFoundException: Could not find org.jetbrains.compose.ui:ui-test-junit4-uikitarm64:1.5.11.

And this one

Screenshot 2024-01-24 at 13 10 15

I have also tried with

@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
        commonTest.dependencies {
            implementation(compose.uiTestJUnit4)
        }

Same issues.

is there anything that I'm missing to test my composables inside my commonMain folder ?

Maven repo : https://mvnrepository.com/artifact/org.jetbrains.compose.ui/ui-test-junit4-desktop

m-sasha commented 4 months ago

What you looked at was the test API for desktop. The test API for common will be available in 1.6.

chrisjenx commented 4 months ago

If you want to write ui tests, currently I have compose testing setup specifically in the android app as android integration tests, obviously that only covers one platform so I would make sure you have more tests for actual implementations on other platforms, but it's useful to check flows/app responds correctly.

Not sure how the common test api will work beyond testing compose at the view level not screen level, but I would probably recommend both. View testing with compose common (when it's out), then use your iOS/Android/Desktop ui testing frameworks to do integration/functional regression tests.

m-sasha commented 4 months ago

The common test API will have the same functionality as the desktop and android test APIs. The main difference is that it will not be based on JUnit.

gastsail commented 4 months ago

@m-sasha any release date estimate ?

m-sasha commented 4 months ago

1.6.0-beta01 was released a few minutes ago. The docs aren't quite ready yet, though.

gastsail commented 4 months ago

Excellent @m-sasha , is there anything else that we need to update aside from compose multiplatform on this 1.6.0-beta01 release ? I mean if there is also need of update of the kotlin compiler plugin, compose version , and so on.

Also, will the code provided in this question be enough to cover UI tests on that version?

m-sasha commented 4 months ago

You will need to add a few new dependencies, and running on Android will need a bit of Gradle magic. The docs will explain, once out.

m-sasha commented 4 months ago

Here's a preliminary version of the documentation

Ahmad-Hamwi commented 4 months ago

@m-sasha

After following the dependency declaration steps, for some reason the uiTest under the compose object cannot be resolved.

compose = "1.6.0-beta01"
compose-compiler = "1.5.8"
compose-plugin = "1.5.11"
commonTest.dependencies {
    implementation(kotlin("test"))

    @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
    implementation(compose.uiTest)
}

Here's a minimal repo that reproduces the problem:

https://github.com/Ahmad-Hamwi/kmp-ui-test-junitless

m-sasha commented 4 months ago

I think compose-plugin should be set to 1.6.0-beta01, and I don't think you even need to set the compose version. I'm not sure where your project's structure comes from. Is it from Fleet?

Ahmad-Hamwi commented 4 months ago

Upgrading the plugin worked flawlessly for me, thank you!

The project is generated from the online wizard: https://kmp.jetbrains.com/ and I ticked "Share UI (with Compose Multiplatform UI framework)". The generated project comes with ".fleet" folder, but I actually use the project in Android Studio not Fleet.

I got a little bit confused by the generated androidx-related compose dependencies, I though they were actually being used, but turns out they're only being used for preview/tooling, and the Jetbrains plugin is the one importing the real compose code.

Ahmad-Hamwi commented 4 months ago

I've cleaned the dependencies in the project and added a sample ui test in this branch like so:

import androidx.compose.material.Text
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.runComposeUiTest
import kotlin.test.Test

class HelloWorldTest {
    @OptIn(ExperimentalTestApi::class)
    @Test
    fun findsHelloWorld() = runComposeUiTest {
        setContent {
            Text("Hello World")
        }

        onNodeWithText("Hello World").assertExists()
    }
}

I'm getting:

Screenshot 2024-01-30 at 12 23 08 AM

Even though my IDE is importing everything just fine.

It's happening when running the test by selecting the "android (local)"

Screenshot 2024-01-30 at 12 15 10 AM

And the tasks that get executed are: Executing tasks: [:composeApp:cleanTestDebugUnitTest, :composeApp:testDebugUnitTest, --tests, HelloWorldTest] in project /Users/ahmad/AndroidStudioProjects/kmp-ui-testing-junitless/composeApp

If I try to run the test using the "iosSimulatorArm64" the test passes just fine:

Screenshot 2024-01-30 at 12 21 25 AM

This branch in the same repo should reproduce this problem: https://github.com/Ahmad-Hamwi/kmp-ui-test-junitless/tree/runComposeUiTest-unresolved-reference

m-sasha commented 4 months ago

You can't currently run Android Instrumented tests from commonTest in Android Studio. Read the last section of the preliminary docs to see how to run it with Gradle.

Ahmad-Hamwi commented 4 months ago

Is there any way of running them as local unit tests?

m-sasha commented 4 months ago

Is there any way of running them as local unit tests?

No, Compose on Android requires the emulator. You can run a local desktop test if you have a desktop source-set.

gastsail commented 4 months ago

I think there is another way without running the emulator that could be done inside common package.

Example, creating a compose test rule with JUnit config

@RunWith(AndroidJUnit4::class)
@Config(
    manifest = Config.NONE,
    sdk = [Build.VERSION_CODES.P],
    instrumentedPackages = [
        "androidx.loader.content"
    ]
)
class FirstTestWithoutEmulatorRunning {
    @get: Rule
    val composeTestRule = createComposeRule()
    // same code as Ahmad sent
....

}

Could this be done inside the commonMain ? @m-sasha

m-sasha commented 4 months ago

I'm not sure I understand.

I don't think Android Compose tests can run with robolectric, so they must run in the emulator.

What do you mean by "done inside commonMain"? We don't write the tests; users do. We could instruct them to add those annotations, but it's better if they didn't need to.

And did you mean commonTest?

gastsail commented 4 months ago

Sorry , yes, @m-sasha commonTest I ment, but I mean that if we can run the instrumented tests inside commonTest at least with these annotations will make a huge difference of waiting until there is an emulator way of doing it (without going to the android package)

For me, personally, I want to test the UI logic, I don't really care if the emulator does run at the moment I test composable logic, being able to do this in commonTest maybe with a multiplatform way with Robolectric would be amazing.

m-sasha commented 4 months ago

For me, personally, I want to test the UI logic, I don't really care if the emulator does run at the moment I test composable logic, being able to do this in commonTest maybe with a multiplatform way with Robolectric would be amazing.

You can run the test as a "desktop" test (if you have a desktop source-set).

As far as I know, running Jetpack Compose tests with robolectric is not supported by Google. At least I couldn't find anything official on it.

gastsail commented 4 months ago

@m-sasha cool thanks a lot Alexander

Ahmad-Hamwi commented 4 months ago

@gastsail I do have an Android Compose project (not KMP), and my test works using Robolectric without a device. Is there a catch in the case of KMP project?

Screenshot 2024-02-16 at 9 55 44 AM
gastsail commented 4 months ago

@Ahmad-Hamwi problem is that there is no multiplatform lib of robolectric, so we cannot include it in kmp yet since it's only platform specific for now