en un clic
write-unit-tests
// Use when writing or structuring unit tests in stripe-android — covers runScenario pattern, fakes, Turbine Flow testing, and Truth assertions
// Use when writing or structuring unit tests in stripe-android — covers runScenario pattern, fakes, Turbine Flow testing, and Truth assertions
Use when writing NetworkRule integration tests in stripe-android — covers testBodyFromFile, inline JSON modification, request matchers, and fixture patterns
Use when writing Compose UI tests in stripe-android — covers composeRule setup, Robolectric annotations, node assertions, and test tag patterns
Use when writing or running Paparazzi screenshot tests in stripe-android — covers PaparazziRule setup, recording/verifying commands, and test structure
Use when creating a fake test implementation in stripe-android — covers FakeClassName pattern, Turbine call tracking, ViewActionRecorder, and ensureAllEventsConsumed validation
| name | write-unit-tests |
| description | Use when writing or structuring unit tests in stripe-android — covers runScenario pattern, fakes, Turbine Flow testing, and Truth assertions |
This skill describes how to structure tests in the Stripe Android SDK using fakes, scenarios, and proper verification patterns.
create-fake skill)runScenario functions to organize test setupensureAllEventsConsumed() on fakes after test blockassertThat(actual).isEqualTo(expected) from Google Truth.test { } syntaxEvery test should follow this pattern:
@Test
fun `test description`() = runScenario(
// Test-specific parameters
config = testConfig
) {
// 1. Configure: Set up fake behaviors (optional)
fakeService.result = expectedResult
// 2. Execute: Call the code under test
val result = systemUnderTest.doSomething()
// 3. Verify: Assert results and check fake calls
assertThat(result).isEqualTo(expected)
assertThat(fakeService.calls.awaitItem()).isEqualTo(expectedCall)
}
// 4. Validation: ensureAllEventsConsumed called automatically by runScenario
Create a runScenario function and a Scenario class at the bottom of your test file:
class MyFeatureTest {
@Test
fun `test case`() = runScenario {
// Test code using scenario fields
assertThat(systemUnderTest.getValue()).isEqualTo(expectedValue)
}
private fun runScenario(
config: Config = defaultConfig,
block: suspend Scenario.() -> Unit,
) = runTest {
// Setup fakes
val fakeRepository = FakeRepository()
val fakeAnalytics = FakeAnalytics()
// Create system under test
val systemUnderTest = MyFeature(
repository = fakeRepository,
analytics = fakeAnalytics,
config = config,
)
// Run test block with scenario context
Scenario(
systemUnderTest = systemUnderTest,
fakeRepository = fakeRepository,
fakeAnalytics = fakeAnalytics,
).apply { block() }
// Validate all fakes
fakeRepository.ensureAllEventsConsumed()
fakeAnalytics.ensureAllEventsConsumed()
}
private data class Scenario(
val systemUnderTest: MyFeature,
val fakeRepository: FakeRepository,
val fakeAnalytics: FakeAnalytics,
)
}
Key Features:
runScenario replaces runTest as the test entry pointensureAllEventsConsumed() called automatically after test block@Test
fun `fetching data returns success when repository succeeds`() = runScenario {
// Configure fake behavior
fakeRepository.dataResult = Result.success(testData)
// Execute
val result = systemUnderTest.fetchData()
// Verify
assertThat(result.isSuccess).isTrue()
assertThat(fakeRepository.fetchCalls.awaitItem()).isEqualTo(FetchCall(userId = "123"))
}
// ensureAllEventsConsumed called automatically
Use Turbine's .test { } to assert Flow emissions:
@Test
fun `state updates when data changes`() = runScenario {
systemUnderTest.state.test {
// Initial state
assertThat(awaitItem()).isEqualTo(State.Loading)
// Trigger change
fakeRepository.emit(newData)
assertThat(awaitItem()).isEqualTo(State.Loaded(newData))
// No more events expected
ensureAllEventsConsumed()
}
}
Common Turbine operations:
awaitItem() — wait for next emissionexpectNoEvents() — assert no emissions occurredensureAllEventsConsumed() — verify no unconsumed events remainskipItems(n) — skip past emissions you don't care about| What | Pattern |
|---|---|
| Test entry point | fun \test name`() = runScenario { }` |
| Assertions | assertThat(actual).isEqualTo(expected) |
| Flow testing | flow.test { assertThat(awaitItem()).isEqualTo(x) } |
| Fake call tracking | assertThat(fake.calls.awaitItem()).isEqualTo(call) |
| Fake validation | ensureAllEventsConsumed() — automatic in runScenario |
| Compose UI tests | Invoke compose-tests skill |
| Creating fakes | Invoke create-fake skill |
| NetworkRule integration tests | Invoke network-tests skill |
FakeClassName implementationsensureAllEventsConsumed() — runScenario handles this, but if using runTest directly, call it manually