rc-testing — Claude Code skill: what it does, how to install
Configures RevenueCat Test Store on Android, wraps the Purchases SDK for MockK unit testing, and sets up headless CI checks in GitHub Actions.
npx skills add RevenueCat/ai-toolkit --skill rc-testing -a claude-codeWhat rc-testing does
Testing Google Play billing during daily development iterations is notoriously slow when relying exclusively on the regular sandbox. This skill implements the RevenueCat Test Store using a test_ API key. Running debug builds presents a modal with Success, Fail, and Cancel choices, letting you verify entitlement activation and UI error states directly on your device.
The Purchases singleton resists direct unit testing. Because of this limitation, the workflow wraps SDK calls inside a lean BillingService interface. ViewModels depend only on this abstraction. That makes state transitions testable with MockK and kotlinx-coroutines-test, while a minimal GitHub Actions job executes ./gradlew testDebugUnitTest on headless Ubuntu runners using an environment secret.
The Test Store does not simulate recurring subscription renewals, parental approval flows, or end-to-end checkout steps. Those specific billing behaviors still demand verification inside Google Play Sandbox prior to shipping.
What it helps with
- simulate purchase success, failure, and cancellation paths without sandbox delays
- wrap the Purchases singleton inside a testable BillingService interface
- write unit tests for ViewModels using MockK and coroutines
- run Android billing tests in GitHub Actions without an emulator
Example prompts for the agent
- Help me set up RevenueCat Test Store in build.gradle.kts for debug builds
- Wrap Purchases into a BillingService interface and write a MockK test for my paywall
- Create a GitHub Actions workflow to run billing unit tests with test keys
How to install rc-testing
1Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-testing -a claude-code2Manually: put the files in place
~/.claude/skills/rc-testing/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/rc-testing/
1Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-testing -a codex2Manually: put the files in place
.agents/skills/rc-testing/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-testing -a cursor2Manually: put the files in place
.cursor/skills/rc-testing/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install RevenueCat/ai-toolkit rc-testing2Manually: put the files in place
.github/skills/rc-testing/SKILL.md1Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-testing -a gemini-cli2Platform CLI
gemini skills install https://github.com/RevenueCat/ai-toolkit --path revenuecat-play-billing/skills/rc-testingManually: put the files in place
.devin/skills/rc-testing/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-testing -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about rc-testing
How to install rc-testing in Claude Code?
Run npx skills add RevenueCat/ai-toolkit --skill rc-testing -a claude-code in your terminal.
What does rc-testing do?
It guides developers through testing RevenueCat on Android using Test Store dialogs, isolating SDK calls behind interfaces for MockK, and setting up CI workflows.
When is Google Play Sandbox still necessary?
You still need Google Play Sandbox to verify subscription renewal cycles, pending parental purchases, and full checkout flows before release.
Source text
SKILL.md · MIT
---
name: rc-testing
description: Use this skill when testing a RevenueCat Android integration. Covers the RevenueCat Test Store (test_ API key prefix, in dialog Success/Fail/Cancel choice), mockk based unit testing with an interface wrapper around Purchases, and a GitHub Actions CI pattern. Avoids Google Play sandbox for day to day test iteration.
license: Apache-2.0; see LICENSE
metadata:
author: RevenueCat
source: revenuecat-handbook chapter 16
keywords:
- android
- revenuecat
- testing
- test-store
- mockk
- github-actions
---
# Testing RevenueCat on Android
Use this skill to stand up a fast test loop for a RevenueCat Android integration. The Test Store replaces the Google Play sandbox for most work, a `BillingService` interface makes `Purchases` mockable, and a small GitHub Actions job runs unit tests on every push.
Full source: see the [full chapter on revenuecat.com](https://www.revenuecat.com/guides/revenuecat-android-sdk/testing).
---
## Phase 1: Discovery
Before changing anything, learn what already exists. Answer each question with a file or a "no".
| Check | Where to look | What you want |
|---|---|---|
| Test API key configured? | `app/build.gradle.kts`, `local.properties`, env | A `test_...` value bound to `BuildConfig.RC_API_KEY` in debug |
| Release key separate? | `build.gradle.kts` release block | A `goog_...` key, never `test_...` |
| Purchases wrapped? | `app/src/main/java/**/*Billing*.kt` | An interface that hides `Purchases.sharedInstance` |
| CI present? | `.github/workflows/*.yml` | A job running `./gradlew testDebugUnitTest` |
| Secret wired? | GitHub repo settings, workflow `env:` | `RC_TEST_STORE_KEY` referenced from `secrets` |
| Test library? | `app/build.gradle.kts` dependencies | `io.mockk:mockk` and `kotlinx-coroutines-test` |
If the project has none of these, start from scratch in Phase 3. If some exist, only fill the gaps.
---
## Phase 2: Plan
Pick the test path per scenario. The Test Store is the default; fall back to Google Play sandbox only when you need store behavior the Test Store does not simulate.
| Scenario | Use |
|---|---|
| Success, failure, cancel paths | Test Store |
| Unit tests of ViewModels | mockk against `BillingService` |
| CI on every push | Test Store + unit tests |
| Subscription renewal cycles | Google Play Sandbox |
| Pending purchase (parental approval) | Google Play Sandbox |
| Full end-to-end payment | Google Play Sandbox |
Write the plan as a short checklist in `tasks/todo.md` before coding. If the app ships subscriptions, keep one Sandbox pass in the pre-ship checklist even if the Test Store covers daily iteration.
---
## Phase 3: Execute
### Step 1: Generate the test API key
In the RevenueCat dashboard, open your app, go to **Apps & providers**, then **Create Test Store**. Copy the `test_...` key. Do not commit it. Put it in `local.properties` or a CI secret.
### Step 2: Wire the key into debug builds
```kotlin
// build.gradle.kts
android {
buildTypes {
debug {
buildConfigField("String", "RC_API_KEY", "\"test_YOUR_KEY\"")
}
release {
buildConfigField("String", "RC_API_KEY", "\"goog_YOUR_KEY\"")
}
}
}
```
For CI, read from env so the key never lands in git:
```kotlin
// build.gradle.kts
val testKey = System.getenv("RC_TEST_STORE_KEY") ?: "test_placeholder"
buildConfigField("String", "RC_API_KEY", "\"$testKey\"")
```
### Step 3: Configure Purchases with the build config key
```kotlin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) Purchases.logLevel = LogLevel.DEBUG
Purchases.configure(
PurchasesConfiguration.Builder(this, BuildConfig.RC_API_KEY).build()
)
}
}
```
### Step 4: Trigger the Test Store dialog
Run the app in debug and call `awaitPurchase()` from your paywall. A dialog appears with Success, Fail, and Cancel options. Each choice resolves the same way production would: Success returns a `PurchaseResult` with active entitlements, Fail throws `PurchasesTransactionException` with a payment error, Cancel throws with `userCancelled = true`. Walk every branch of your error handling to verify UI states.
### Step 5: Wrap Purchases in a BillingService
The `Purchases` singleton is not mockable. A thin interface lets you inject a fake in tests and keeps ViewModels free of SDK types.
```kotlin
interface BillingService {
suspend fun getOfferings(): Offerings
suspend fun purchase(activity: Activity, pkg: Package): CustomerInfo
suspend fun getCustomerInfo(): CustomerInfo
}
```
```kotlin
class RevenueCatBillingService : BillingService {
override suspend fun getOfferings() =
Purchases.sharedInstance.awaitOfferings()
override suspend fun purchase(activity: Activity, pkg: Package): CustomerInfo =
Purchases.sharedInstance.awaitPurchase(
PurchaseParams.Builder(activity, pkg).build()
).customerInfo
override suspend fun getCustomerInfo() =
Purchases.sharedInstance.awaitCustomerInfo()
}
```
### Step 6: Write mockk unit tests
```kotlin
class PaywallViewModelTest {
private val billing = mockk<BillingService>()
private val viewModel = PaywallViewModel(billing)
@Test
fun `purchase success grants access`() = runTest {
val info = mockk<CustomerInfo> {
every { entitlements["pro_access"]?.isActive } returns true
}
coEvery { billing.purchase(any(), any()) } returns info
viewModel.purchase(mockActivity, mockPackage)
assertTrue(viewModel.state.value is PaywallState.Success)
}
}
```
Add one test per branch: success grants access, failure shows error, cancel stays idle.
### Step 7: Add a minimal GitHub Actions job
```yaml
# .github/workflows/test.yml
name: test
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Run tests
env:
RC_TEST_STORE_KEY: ${{ secrets.RC_TEST_STORE_KEY }}
run: ./gradlew testDebugUnitTest
```
Store the `test_...` key as the `RC_TEST_STORE_KEY` repository secret. The job runs without a device or a Google account.
---
## Verification
After each purchase, open the RevenueCat dashboard, go to **Customers**, pick the user, and confirm the purchase, the entitlement, and the `CustomerInfo` JSON. The **Events** tab shows webhooks fired for the test purchase.
## Pre-Ship Checklist
- [ ] `Purchases.logLevel = LogLevel.DEBUG` gated on `BuildConfig.DEBUG`
- [ ] Release build uses the `goog_` key, not the `test_` key
- [ ] `test_` key is not committed (env var or `local.properties`)
- [ ] Success, failure, and cancel paths exercised through the Test Store dialog
- [ ] At least one end-to-end flow verified in Google Play Sandbox
- [ ] Webhook endpoint receives events for sandbox purchases
## References
- [Full chapter](https://www.revenuecat.com/guides/revenuecat-android-sdk/testing)
© RevenueCat · MIT · RevenueCat/ai-toolkit