reactormonk
09/25/2025, 2:00 PMOliver.O
09/25/2025, 2:11 PMreactormonk
09/25/2025, 2:12 PMOliver.O
09/25/2025, 2:14 PMreactormonk
09/25/2025, 2:15 PMreactormonk
09/25/2025, 2:15 PMOliver.O
09/25/2025, 2:19 PMreactormonk
09/25/2025, 2:20 PMOliver.O
09/25/2025, 2:22 PMpackage com.example
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import de.infix.testBalloon.framework.TestDiscoverable
import de.infix.testBalloon.framework.TestSuite
import de.infix.testBalloon.framework.testSuite
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.test.runTest
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
val ComposeTestsWithTestBalloon by testSuite {
@Composable
fun ComposableUnderTest() {
var text by remember { mutableStateOf("Idle") }
Button(onClick = { text = "Success" }) {
Text("Button")
}
Text(text)
}
testWithCompose("setup") {
composeTestRule.setContent {
ComposableUnderTest()
}
composeTestRule.onNodeWithText("Idle").assertExists()
}
testWithCompose("click") {
composeTestRule.setContent {
ComposableUnderTest()
}
composeTestRule.onNodeWithText("Button").performClick()
composeTestRule.awaitIdle()
composeTestRule.onNodeWithText("Success").assertExists()
}
}
/**
* Declares a [Test] with a [ComposeTestContext] as a child of this test suite.
*/
@TestDiscoverable
fun TestSuite.testWithCompose(
name: String,
composeTestRule: ComposeContentTestRule = createComposeRule(),
action: suspend ComposeTestContext.() -> Unit
) = testWithJUnitRule(name, composeTestRule) {
ComposeTestContext(composeTestRule).action()
}
class ComposeTestContext(val composeTestRule: ComposeContentTestRule)
/**
* Declares a [Test] wrapped by a JUnit 4 [TestRule] as a child of this test suite.
*/
@TestDiscoverable
fun <Rule : TestRule> TestSuite.testWithJUnitRule(name: String, rule: Rule, action: suspend (Rule) -> Unit) =
test(name) {
val description = Description.createTestDescription(
"${this@testWithJUnitRule.testElementPath}",
name,
"$testElementPath"
)
rule.apply(
object : Statement() {
override fun evaluate() {
runTest(coroutineContext.minusKey(CoroutineExceptionHandler.Key)) { action(rule) }
}
},
description
).evaluate()
}
Oliver.O
09/25/2025, 2:24 PMOliver.O
09/25/2025, 2:27 PMreactormonk
09/25/2025, 2:31 PM