network-tests
$
npx mdskill add stripe/stripe-android/network-testsGenerate Stripe Android network integration tests with mocked responses.
- Enables developers to write tests requiring mocked network responses.
- Integrates with NetworkRule and testBodyFromFile for JSON handling.
- Selects fixture files from src/androidTest/resources by filename.
- Delivers runnable test classes ready for instrumentation testing.
SKILL.md
.github/skills/network-testsView on GitHub ↗
---
name: network-tests
description: Use when writing NetworkRule integration tests in stripe-android — covers testBodyFromFile, inline JSON modification, request matchers, and fixture patterns
---
# NetworkRule Integration Tests
For instrumentation tests that need mocked network responses, use `NetworkRule` (from `network-testing` module) with `testBodyFromFile`. JSON fixture files live in the module's `src/androidTest/resources/` directory (e.g., `paymentsheet/src/androidTest/resources/checkout-session-init.json`) and are resolved by filename from the resources root.
## Basic Structure
```kotlin
@RunWith(AndroidJUnit4::class)
internal class MyFeatureTest {
@get:Rule
val testRules: TestRules = TestRules.create()
private val networkRule = testRules.networkRule
@Test
fun testSomething() = runPaymentSheetTest(
networkRule = networkRule,
resultCallback = ::assertCompleted,
) { testContext ->
networkRule.enqueue(requestMatcher) { response ->
response.testBodyFromFile("my-fixture.json")
}
// ... test actions ...
}
}
```
## Prefer Inline JSON Modification Over New Fixture Files
When a test needs a modified JSON response, use the `testBodyFromFile` lambda to modify the base fixture inline — do NOT create a separate JSON file for each variation.
```kotlin
// GOOD: Modify the base fixture inline
networkRule.checkoutInit { response ->
response.testBodyFromFile("checkout-session-init.json") { json ->
json.put("customer_email", "session@example.com")
}
}
// BAD: Creating checkout-session-init-with-email.json with one field different
networkRule.checkoutInit { response ->
response.testBodyFromFile("checkout-session-init-with-email.json")
}
```
This keeps the fixture set minimal and makes the test-specific modifications explicit at the call site.
## Composing Multiple Modifications
The lambda receives a `JSONObject` — use standard `org.json` methods to add or modify fields:
```kotlin
networkRule.checkoutInit { response ->
response.testBodyFromFile("checkout-session-init.json") { json ->
json.put("customer", JSONObject("""
{
"id": "cus_12345",
"payment_methods": [],
"can_detach_payment_method": true
}
""".trimIndent()))
json.put("customer_managed_saved_payment_methods_offer_save", JSONObject("""
{"enabled": true, "status": "not_accepted"}
""".trimIndent()))
}
}
```
For nested modifications, chain `getJSONObject()`:
```kotlin
response.testBodyFromFile("checkout-session-init.json") { json ->
json.getJSONObject("server_built_elements_session_params")
.getJSONObject("deferred_intent")
.put("setup_future_usage", "off_session")
}
```
## Extracting Shared Modifiers
When multiple tests share the same JSON modification, extract the lambda as a parameter:
```kotlin
private fun runMyTest(
jsonModifier: (JSONObject) -> Unit = {},
) = runPaymentSheetTest(networkRule = networkRule, resultCallback = ::assertCompleted) { testContext ->
networkRule.checkoutInit { response ->
response.testBodyFromFile("checkout-session-init.json", jsonModifier)
}
// ... shared test logic ...
}
@Test
fun testWithSfu() = runMyTest { json ->
json.getJSONObject("server_built_elements_session_params")
.getJSONObject("deferred_intent")
.put("setup_future_usage", "off_session")
}
```
## testBodyFromFile Variants
| Signature | Use when |
|-----------|----------|
| `testBodyFromFile("file.json")` | No modifications needed |
| `testBodyFromFile("file.json") { json -> ... }` | Modifying JSON fields inline |
| `testBodyFromFile("file.json", replacements)` | String-level find/replace with `ResponseReplacement` |
## Request Matchers
Use `RequestMatchers` (from `com.stripe.android.networktesting.RequestMatchers`) to validate request body parameters. Import the matchers you need statically:
```kotlin
import com.stripe.android.networktesting.RequestMatchers.bodyPart
import com.stripe.android.networktesting.RequestMatchers.hasBodyPart
import com.stripe.android.networktesting.RequestMatchers.not
networkRule.checkoutConfirm(
bodyPart("expected_amount", "5099"),
not(hasBodyPart("save_payment_method")),
) { response ->
response.testBodyFromFile("checkout-session-confirm.json")
}
```
### bodyPart Encoding
`bodyPart()`, `hasBodyPart()`, and `query(name, value)` auto-decode both the matcher arguments and the request body before comparing. Use plain readable strings — `urlEncode()` is unnecessary:
```kotlin
// Keys with brackets and values with special characters — just use plain strings
bodyPart("billing_details[email]", "user@example.com")
bodyPart("billing_details[address][line1]", "123 Main St")
bodyPart("payment_method_data[allow_redisplay]", "unspecified")
// urlEncode() still works (backward compatible) but is unnecessary
// bodyPart(urlEncode("billing_details[email]"), urlEncode("user@example.com"))
```
### Mismatch Debugging
When a request doesn't match a mock, the error message shows per-matcher diagnostics with the nearest-miss mock:
```
POST https://localhost/v1/payment_intents/pi_123/confirm
Body params: {billing_details[email]=actual@test.com, payment_method=pm_123}
Nearest mock: composite(path(/v1/confirm), bodyPart(billing_details[email], expected@test.com))
+ PASS: path(/v1/confirm)
+ PASS: method(POST)
- FAIL: bodyPart(billing_details[email], expected@test.com)
```
See `PaymentSheetBillingConfigurationTest.kt` for more examples.
More from stripe/stripe-android
- compose-testsUse when writing Compose UI tests in stripe-android — covers composeRule setup, Robolectric annotations, node assertions, and test tag patterns
- create-fakeUse when creating a fake test implementation in stripe-android — covers FakeClassName pattern, Turbine call tracking, ViewActionRecorder, and ensureAllEventsConsumed validation
- screenshot-testsUse when writing or running Paparazzi screenshot tests in stripe-android — covers PaparazziRule setup, recording/verifying commands, and test structure
- write-unit-testsUse when writing or structuring unit tests in stripe-android — covers runScenario pattern, fakes, Turbine Flow testing, and Truth assertions