Test Store Genel Bakış

0:02:00

Android icin RevenueCat Test Store kurulum kilavuzuna hos geldiniz!

Uygulama ici satin alma testleri her zaman zor olmustur. Sandbox hesaplari kurmak, test urunleri olusturmak, uygulama incelemelerini beklemek ve guvenilmez ag kosullarini yonetmek gerekiyordu. RevenueCat'in Test Store'u belirleyici, hizli ve guvenilir bir test ortami saglayarak bu sorunlari cozer.

Test Store Nedir?

Test Store, Google Play Store'a baglanmadan uygulama ici satin alma akislarini test etmenizi saglayan RevenueCat'in yerlesik test ortamidir. Tum yeni RevenueCat projelerine otomatik olarak saglanir ve satin alma sonuclari uzerinde tam kontrol sunar.

Ogrenecekleriniz

Bu codelab'de Android gelistirme icin RevenueCat'in Test Store kurulumu ve kullaniminin tum surecini ogreneceksiniz. RevenueCat panelinde Test Store'u etkinlestirmek ve Android uygulamanizi Test Store API anahtarini kullanacak sekilde yapilandirmakla baslayacaksiniz. Ardindan belirleyici sonuclarla satin alma akislarini test etmeyi, uygulama ici satin alma mantigi icin otomatik birim testleri yazmayi ve GitHub Actions kullanarak bu testleri CI/CD pipeline'iniza entegre etmeyi ogreneceksiniz.

Test Store'un Avantajlari

Test Store, uygulama ici satin alma test yontemini devrim niteliginde degistiriyor. Google Play kurulumu veya uygulama onayini beklemenizi gerektiren mevcut test yontemlerinin aksine, Test Store anlik test yetenekleri sunar. Satin alma sonuclari uzerinde tam belirleyici kontrol saglar. Islemin basarili olup olmayacagi, basarisiz olup olmayacagi veya iptal edilip edilmeyecegi tamamen size baglidir. Bu, guvenilmez ag baglantilari veya guvenilmez sandbox ortamlariyla ugrasmaniza gerek olmadigini gosterir.

Gercek guc, otomatik testler yazmayi baslattiginizda ortaya cikar. Test Store'u kullanarak GitHub Actions gibi CI/CD sistemleri dahil tum ortamlarda tutarli olarak calisan satin alma mantigi icin guvenilir birim testleri olusturabilirsiniz. Bu sayede satin alma akislarini hizla yineleyerek degisiklikleri dakikalar veya saatler yerine saniyeler icinde test edebilirsiniz.

overview

On Kosullar

Baslamadan once asagidakileri kontrol edin:

  1. RevenueCat hesabi (revenuecat.com'da ucretsiz)
  2. RevenueCat SDK entegre edilmis Android projesi (surum 9.0.0 veya ustuu)
  3. Kotlin ve Android gelistirme hakkinda temel bilgi
  4. Jetpack Compose hakkinda anlayis (istege bagli, UI ornekleri icin)

Panelde Test Store'u Etkinlestirme

0:03:00

Ilk adim, RevenueCat panelinde Test Store'u etkinlestirmek ve Test Store API anahtarini almaktir.

Panele Erisim

RevenueCat paneline giris yapin ve sol kenar cubugu menusundeki Apps & providers bolumune gidin. Burada bagli tum uygulamalarinizi ve mevcut magaza entegrasyonlarinizi bulabilirsiniz.

Test Store Olusturma

Apps & providers bolumunde mevcut saglayicilar arasinda Test Store secenegini bulun. Test Store tum RevenueCat projelerine otomatik olarak saglandigi icin Create Test Store veya Enable Test Store'a tiklayarak etkinlestirmeniz yeterlidir. Kurulum onayi veya yapilandirma senkronizasyonu beklemenize gerek yoktur; aninda tamamlanir.

API Anahtarini Alma

Test Store etkinlestirildiginde, uygulama listesinde Test Store girişine tiklayarak ayrintilari kontrol edin. Burada test_ onekiyle baslayan Test Store API Key'i bulabilirsiniz. Bu anahtar normal RevenueCat API anahtari gibi calisir, ancak onemli bir fark vardir: tum satin alma isteklerini Google Play yerine Test Store'a yonlendirerek test ortami uzerinde tam kontrol saglar.

API Anahtari Ayrimini Anlama: Test Store API anahtari, production ve sandbox anahtarlarindan tamamen ayridir. Bu ayrim kasitli ve guvenlik acisindan onemlidir. Production build'lerde asla Test Store anahtarini kullanmayin; yalnizca debug ve test ortamlarinda kullanin. Gerekirse farkli test senaryolari icin proje basina birden fazla Test Store yapilandirmasi olusturabilirsiniz.

test-store-setup

Sonraki adım

Artik Test Store API anahtarini aldiginiza gore, Android uygulamanizi test icin Test Store'u kullanacak sekilde yapilandirmaya hazirsiniz.

Android Uygulamada Test Store Kurulumu

0:05:00

Simdi Android uygulamanizi Test Store'u kullanacak sekilde yapilandiracagiz. Onemli testleri calistirirken production API anahtari yerine Test Store API anahtarini kullanacaksiniz.

Adim 1: BuildConfig Alani Olusturma

Uygulamanizin build.gradle.kts dosyasina Test Store API anahtari yapilandirmasini ekleyin:

kotlin
android {
    defaultConfig {
        // Your existing configuration

        // Add Test Store API key for debug builds
        buildConfigField("String", "REVENUECAT_TEST_STORE_API_KEY", "\"test_YOUR_KEY_HERE\"")
    }

    buildTypes {
        debug {
            // Use Test Store for debug builds
            buildConfigField("String", "REVENUECAT_API_KEY", "\"test_YOUR_KEY_HERE\"")
        }
        release {
            // Use production API key for release builds
            buildConfigField("String", "REVENUECAT_API_KEY", "\"goog_YOUR_PRODUCTION_KEY\"")
        }
    }
}

Adim 2: Test Store ile SDK Baslatma

Build turune gore uygun API anahtarini kullanmak icin Application sinifinizi guncelleyin:

kotlin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // Initialize RevenueCat SDK
        val apiKey = BuildConfig.REVENUECAT_API_KEY

        val builder = PurchasesConfiguration.Builder(this, apiKey)
        Purchases.configure(
            builder
                .purchasesAreCompletedBy(PurchasesAreCompletedBy.REVENUECAT)
                .appUserID(null)
                .diagnosticsEnabled(true)
                .build()
        )

        // Enable debug logs for test builds
        if (BuildConfig.DEBUG) {
            Purchases.logLevel = LogLevel.DEBUG
        }
    }
}

Adim 3: Ortama Ozel Yapilandirma

Gelismis yapilandirma icin bir yapilandirma sinifi olusturabilirsiniz:

kotlin
object RevenueCatConfig {
    fun getApiKey(context: Context): String {
        return if (isTestEnvironment()) {
            BuildConfig.REVENUECAT_TEST_STORE_API_KEY
        } else {
            BuildConfig.REVENUECAT_API_KEY
        }
    }

    private fun isTestEnvironment(): Boolean {
        // Check if running in test environment
        return try {
            Class.forName("androidx.test.espresso.Espresso")
            true
        } catch (e: ClassNotFoundException) {
            false
        } || BuildConfig.DEBUG
    }
}

Ardindan baslatma sirasinda kullanin:

kotlin
val apiKey = RevenueCatConfig.getApiKey(this)
Purchases.configure(
    PurchasesConfiguration.Builder(this, apiKey)
        .purchasesAreCompletedBy(PurchasesAreCompletedBy.REVENUECAT)
        .build()
)

doğrulama

Test Store'un calisip calismadigini dogrulamak icin:

  1. Uygulamayi debug modunda calistirin
  2. Gunluklerde "Purchases SDK initialized with Test Store" mesajini kontrol edin
  3. SDK'nin Google Play yerine Test Store'a baglandigini dogrulayin
test-store-config

Etkilesimli Satin Alma Akisi Testi

0:05:00

Test Store etkinlestirildiginde, artik sonuclari tam olarak kontrol ederek satin alma akislarini test edebilirsiniz. Test Store satin alma iletisim kutusunu gosterdiginde sonucu siz belirlersiniz.

Test Store Satin Alma Iletisim Kutusunu Anlama

Test Store ile bir satin alma baslattiginizda, standart Google Play satin alma sayfasi yerine ozel bir Test Store iletisim kutusu gosterilir. Bu iletisim kutusu, gercek satin alma iletisim kutusu gibi urun ayrintilari ve fiyatlandirmayi gosterir, ancak onemli bir fark vardir: sonucu kontrol edebilirsiniz. Iletisim kutusu uc net secenek sunar: tamamlanmis islemi simule eden Successful Purchase, satin alma basarisizligini test eden Failed Purchase ve kullanici iptalini simule eden Cancel. Bu belirleyici davranis, Test Store'u test icin son derece guclu kilan seydir.

Basarili Akisi Test Etme

Basarili bir satin alma akisini test edelim:

kotlin
suspend fun purchaseProduct(activity: Activity) {
    try {
        // Fetch products from Test Store
        val products = Purchases.sharedInstance.awaitGetProducts(
            productIds = listOf("premium_monthly")
        )

        // Initiate purchase
        val purchaseResult = Purchases.sharedInstance.awaitPurchase(
            purchaseParams = PurchaseParams.Builder(
                activity = activity,
                storeProduct = products.first()
            ).build()
        )

        // Check the result
        val customerInfo = purchaseResult.customerInfo
        val isPremium = customerInfo.entitlements["premium"]?.isActive == true

        if (isPremium) {
            // Purchase successful!
            showPremiumContent()
        }
    } catch (e: PurchasesException) {
        // Handle error
        handlePurchaseError(e)
    }
}

Mutlu Yol Testi: Uygulamayi calistirin ve her zamanki gibi satin alma akisini baslatin. Test Store iletisim kutusu gorundigunde "Successful Purchase"'a dokunun. Uygulama aninda premium erisim vermeli ve yetkinin dogru guncellendigini dogrulayabilirsiniz. Bu sayede gercek odeme yontemi veya sandbox hesabi olmadan basarili akisin dogru calisip calismadigini hizlica kontrol edebilirsiniz.

Basarisiz Senaryolari Test Etme

Simdi uygulamanizin basarisizliklari nasil islemedigini test edin:

kotlin
fun handlePurchaseError(error: PurchasesException) {
    when (error.code) {
        PurchasesErrorCode.PURCHASE_CANCELLED_ERROR -> {
            // User cancelled - don't show error
            Log.d("Purchase", "User cancelled purchase")
        }
        PurchasesErrorCode.PURCHASE_INVALID_ERROR -> {
            // Invalid purchase
            showError("This purchase is not available")
        }
        PurchasesErrorCode.PAYMENT_PENDING_ERROR -> {
            // Payment pending (e.g., awaiting parental approval)
            showPendingMessage()
        }
        else -> {
            // Other errors
            showError("Purchase failed: ${error.message}")
        }
    }
}

Hata Isleme Testi: Satin alma akisini tekrar baslatin, ancak bu sefer Test Store iletisim kutusu gorundigunde "Failed Purchase"'a dokunun. Uygulama hatayi duygusuzca islemeli ve kullaniciya uygun bir hata mesaji gostermelidir. Kullanicinin premium erisim almadigini ve uygulamanin durumunun tutarli kaldigini dogrulayin. Bu, production'da hata isleme mantginizin dogru calisip calismadigini kontrol etmek icin onemlidir.

Iptali Test Etme

kotlin
@Composable
fun PaywallScreen(onDismiss: () -> Unit) {
    val scope = rememberCoroutineScope()
    var isPurchasing by remember { mutableStateOf(false) }

    Button(
        onClick = {
            scope.launch {
                isPurchasing = true
                try {
                    purchaseProduct(LocalContext.current as Activity)
                } catch (e: PurchasesException) {
                    if (e.code == PurchasesErrorCode.PURCHASE_CANCELLED_ERROR) {
                        // User cancelled - just dismiss
                        onDismiss()
                    }
                }
                isPurchasing = false
            }
        },
        enabled = !isPurchasing
    ) {
        Text(if (isPurchasing) "Processing..." else "Subscribe")
    }
}

Kullanici Iptali Testi: Odeme duvarini acin ve abonelik butonuna dokunun. Test Store iletisim kutusu gorundigunde, satin almayi iptal eden bir kullaniciyi simule etmek icin "Cancel"'a dokunun. Odeme duvari hata mesaji gostermeden temiz bir sekilde kapanmalidir (iptal, bir hata durumu degil, normal bir kullanici eylemidir). Satin alma islemi kaydedilmedigini ve uygulamanin durumunun degismedigini dogrulayin.

Temel Cikarimlar

Test Store'un gucu belirleyici kontroldedir. Her satin alma girisiminde tam olarak ne olacagina siz karar verirsiniz. Bu, gercek odeme yontemi gerektirmeden veya sandbox hesaplarinin gecikmeleri ve karmasikliklarini yonetmeden dakikalar icinde tum senaryolari (basarili, basarisiz ve iptal) kapsamli bir sekilde test edebileceginiz anlamina gelir. Google Play ile entegre etmeye hazir oldugunuzda, satin alma mantginizin saglam olduguna dair tam bir guven duyabilirsiniz.

test-store-testing

Otomatik Birim Testleri Yazma

0:08:00

Test Store'un en guclu ozelliklerinden biri, uygulama ici satin alma mantigi icin otomatik birim testleri yazmanizi saglamasidir. CI/CD'de guvenilir bir sekilde calisan testler yazalim.

Adim 1: Test Bagimliklarini Ekleme

build.gradle.kts dosyasina asagidakileri ekleyin:

kotlin
dependencies {
    // RevenueCat SDK
    implementation("com.revenuecat.purchases:purchases:9.20.2")

    // Testing dependencies
    testImplementation("junit:junit:4.13.2")
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
    testImplementation("io.mockk:mockk:1.13.8")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test:runner:1.5.2")
}

Adim 2: Purchase Repository Olusturma

Oncelikle test edilebilir bir purchase repository olusturalim:

kotlin
interface PurchaseRepository {
    suspend fun getProducts(productIds: List<String>): List<StoreProduct>
    suspend fun purchaseProduct(activity: Activity, product: StoreProduct): CustomerInfo
    suspend fun getCustomerInfo(): CustomerInfo
}

class PurchaseRepositoryImpl(
    private val purchases: Purchases = Purchases.sharedInstance
) : PurchaseRepository {

    override suspend fun getProducts(productIds: List<String>): List<StoreProduct> {
        return purchases.awaitGetProducts(productIds)
    }

    override suspend fun purchaseProduct(
        activity: Activity,
        product: StoreProduct
    ): CustomerInfo {
        val result = purchases.awaitPurchase(
            PurchaseParams.Builder(activity, product).build()
        )
        return result.customerInfo
    }

    override suspend fun getCustomerInfo(): CustomerInfo {
        return purchases.awaitCustomerInfo()
    }
}

Adim 3: Is Mantigi Iceren View Model Olusturma

kotlin
class PaywallViewModel(
    private val repository: PurchaseRepository
) : ViewModel() {

    private val _state = MutableStateFlow<PaywallState>(PaywallState.Loading)
    val state: StateFlow<PaywallState> = _state.asStateFlow()

    fun loadProducts() {
        viewModelScope.launch {
            try {
                val products = repository.getProducts(listOf("premium_monthly"))
                _state.value = PaywallState.Success(products)
            } catch (e: Exception) {
                _state.value = PaywallState.Error(e.message ?: "Unknown error")
            }
        }
    }

    fun purchaseProduct(activity: Activity, product: StoreProduct) {
        viewModelScope.launch {
            _state.value = PaywallState.Purchasing
            try {
                val customerInfo = repository.purchaseProduct(activity, product)
                val isPremium = customerInfo.entitlements["premium"]?.isActive == true

                if (isPremium) {
                    _state.value = PaywallState.PurchaseSuccess
                } else {
                    _state.value = PaywallState.Error("Purchase completed but entitlement not active")
                }
            } catch (e: PurchasesException) {
                when (e.code) {
                    PurchasesErrorCode.PURCHASE_CANCELLED_ERROR -> {
                        _state.value = PaywallState.PurchaseCancelled
                    }
                    else -> {
                        _state.value = PaywallState.Error(e.message)
                    }
                }
            }
        }
    }
}

sealed class PaywallState {
    object Loading : PaywallState()
    data class Success(val products: List<StoreProduct>) : PaywallState()
    object Purchasing : PaywallState()
    object PurchaseSuccess : PaywallState()
    object PurchaseCancelled : PaywallState()
    data class Error(val message: String) : PaywallState()
}

Adim 4: Birim Testleri Yazma

Simdi Test Store'u kullanarak birim testleri olusturun:

kotlin
@RunWith(AndroidJUnit4::class)
class PaywallViewModelTest {

    private lateinit var repository: PurchaseRepository
    private lateinit var viewModel: PaywallViewModel

    @Before
    fun setup() {
        // Initialize RevenueCat with Test Store API key
        Purchases.configure(
            PurchasesConfiguration.Builder(
                ApplicationProvider.getApplicationContext(),
                "test_YOUR_KEY_HERE"
            ).build()
        )

        repository = PurchaseRepositoryImpl()
        viewModel = PaywallViewModel(repository)
    }

    @Test
    fun testLoadProducts_Success() = runTest {
        // Given: ViewModel is initialized

        // When: Loading products
        viewModel.loadProducts()

        // Wait for state to update
        advanceUntilIdle()

        // Then: Products should be loaded successfully
        val state = viewModel.state.value
        assertTrue(state is PaywallState.Success)
        assertFalse((state as PaywallState.Success).products.isEmpty())
    }

    @Test
    fun testPurchaseProduct_Success() = runTest {
        // Given: Products are loaded
        viewModel.loadProducts()
        advanceUntilIdle()

        val state = viewModel.state.value as PaywallState.Success
        val product = state.products.first()

        // When: User purchases product and Test Store dialog shows "Successful Purchase"
        // Note: In actual test, you'll need to interact with Test Store dialog
        viewModel.purchaseProduct(mockActivity, product)
        advanceUntilIdle()

        // Then: Purchase should succeed and entitlement should be active
        val finalState = viewModel.state.value
        assertTrue(finalState is PaywallState.PurchaseSuccess)
    }

    @Test
    fun testPurchaseProduct_Cancelled() = runTest {
        // Given: Products are loaded
        viewModel.loadProducts()
        advanceUntilIdle()

        val state = viewModel.state.value as PaywallState.Success
        val product = state.products.first()

        // When: User cancels purchase in Test Store dialog
        viewModel.purchaseProduct(mockActivity, product)
        advanceUntilIdle()

        // Then: State should indicate cancellation
        val finalState = viewModel.state.value
        assertTrue(finalState is PaywallState.PurchaseCancelled)
    }
}

Adim 5: Mock Tabanli Test Alternatifi

Daha fazla kontrol icin repository'yi mock'layabilirsiniz:

kotlin
class PaywallViewModelMockTest {

    private lateinit var mockRepository: PurchaseRepository
    private lateinit var viewModel: PaywallViewModel

    @Before
    fun setup() {
        mockRepository = mockk()
        viewModel = PaywallViewModel(mockRepository)
    }

    @Test
    fun testPurchaseSuccess() = runTest {
        // Given: Mock successful purchase
        val mockProduct = mockk<StoreProduct>()
        val mockCustomerInfo = mockk<CustomerInfo> {
            every { entitlements["premium"]?.isActive } returns true
        }

        coEvery {
            mockRepository.purchaseProduct(any(), mockProduct)
        } returns mockCustomerInfo

        // When: Purchase is initiated
        viewModel.purchaseProduct(mockk(), mockProduct)
        advanceUntilIdle()

        // Then: State should be success
        assertTrue(viewModel.state.value is PaywallState.PurchaseSuccess)
    }

    @Test
    fun testPurchaseFailed() = runTest {
        // Given: Mock failed purchase
        val mockProduct = mockk<StoreProduct>()

        coEvery {
            mockRepository.purchaseProduct(any(), mockProduct)
        } throws PurchasesException(
            PurchasesErrorCode.PAYMENT_PENDING_ERROR,
            "Payment is pending"
        )

        // When: Purchase is initiated
        viewModel.purchaseProduct(mockk(), mockProduct)
        advanceUntilIdle()

        // Then: State should show error
        val state = viewModel.state.value
        assertTrue(state is PaywallState.Error)
        assertTrue((state as PaywallState.Error).message.contains("pending"))
    }
}

Bu Yaklasim Neden Calisiyor

Bu test yaklasimi, uygulama ici satin alma testinin geleneksel sorunlarini ortadan kaldirir. Test Store'a ag bagimliligi olmadigindan testler her seferinde guvenilir bir sekilde calisir. Artik baglanti sorunlari nedeniyle tutarsiz basarisizliklar yasamazsiniz. Testler dakikalar yerine saniyeler icinde calisarak gelistirme sirasinda hizli geri bildirim saglar. Gercek satin alma sistemleriyle yeniden uretmesi zor veya zaman alici uc durumlar dahil tum senaryolarda tam test kapsamini elde edebilirsiniz.

Olusturdugumuz mimari (is mantgini SDK'dan ayiran repository deseni kullanarak) bu testleri hizli ve bakimi kolay kilar. GitHub Actions gibi herhangi bir CI/CD ortaminda calistirabilirsiniz, boylece production'a ulasmadan once hatalari yakalayabilirsiniz.

CI/CD'de Testleri Calistirma

0:04:00

Simdi testlerin GitHub Actions'da otomatik olarak calismasini ayarlayacagiz, boylece her kod degisikliginde satin alma mantigi tutarli bir sekilde korunur.

Adim 1: GitHub Actions Workflow Olusturma

.github/workflows/android-tests.yml dosyasini olusturun:

yaml
name: Android Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  test:
    runs-on: ubuntu-latest

    adım:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: &#039;17'
          distribution: &#039;temurin'

      - name: Cache Gradle packages
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles(&#039;**__RCPH_0_HPRC__gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Run unit tests
        env:
          REVENUECAT_TEST_STORE_API_KEY: ${{ secrets.REVENUECAT_TEST_STORE_API_KEY }}
        run: ./gradlew testDebugUnitTest

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: test-results
          path: app/build/test-results/

      - name: Upload test reports
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: test-reports
          path: app/build/reports/tests/

Adim 2: GitHub Secrets'a Test Store API Anahtarini Ekleme

  1. GitHub repository'nize gidin
  2. SettingsSecrets and variablesActions bolumune gidin
  3. New repository secret'a tiklayin
  4. Ad: REVENUECAT_TEST_STORE_API_KEY
  5. Deger: Test Store API anahtariniz (ornegin: test_xxxxx)
  6. Add secret'a tiklayin

Adim 3: Secret'i Kullanmak icin Gradle Yapilandirmasi

Ortam degiskenini kullanmak icin build.gradle.kts dosyasini guncelleyin:

kotlin
android {
    defaultConfig {
        // Get Test Store API key from environment or use placeholder
        val testStoreApiKey = System.getenv("REVENUECAT_TEST_STORE_API_KEY")
            ?: "test_placeholder"

        buildConfigField("String", "REVENUECAT_TEST_STORE_API_KEY", "\"$testStoreApiKey\"")
    }
}

Adim 4: Emulator ile Instrumented Testleri Calistirma

Gelismis testler icin instrumented testler ekleyin:

yaml
instrumented-test:
    runs-on: ubuntu-latest

    adım:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: &#039;17'
          distribution: &#039;temurin'

      - name: Enable KVM (for faster emulator)
        run: |
          echo &#039;KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Run instrumented tests
        uses: reactivecircus/android-emulator-runner@v2
        env:
          REVENUECAT_TEST_STORE_API_KEY: ${{ secrets.REVENUECAT_TEST_STORE_API_KEY }}
        with:
          api-level: 29
          target: default
          arch: x86_64
          profile: Nexus 6
          script: ./gradlew connectedDebugAndroidTest

      - name: Upload instrumented test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: instrumented-test-results
          path: app/build/outputs/androidTest-results/

Kurulumu Dogrulama

Degisiklikleri commit ve push ederek workflow'u tetikleyin. Repository'nizin GitHub Actions sekmesine giderek testlerin otomatik olarak calistigini dogrulayin. Workflow kodu checkout ederken, ortami kurarken ve test paketini calistirirken gercek zamanli ilerlemeyi gorebilirsiniz. Birkac dakika icinde CI ortaminda tum testlerin gecip gecmedigini ogreneceksiniz.

Otomatik Testlerin Gucu

Bir kez kuruldugunada, her pull request birlestirilmeden once otomatik olarak dogrulanir. Bir degisiklik satin alma mantgini bozarsa aninda geri bildirim alirsiniz ve tum senaryolari manuel olarak test etmeniz gerekmez. Test Store, her calistirmada testlerin tutarli sonuclar uretmesini saglar ve tutarsiz testlerin hayal kirikligini ortadan kaldirir. Bu, hatali kodun main branch'e ulasmasini engelleyen bir kalite kapisi olusturur ve tekrarlayan manuel testlere harcanacak sayisiz saat tasarruf saglar.

En Iyi Uygulamalar ve Production Kurulumu

0:03:00

Test Store'u kurdugunuza gore, simdi en iyi uygulamalari ve Test Store ile production ortami arasinda nasil gecis yapacaginizi ele alacagiz.

Ortam Ayirimi

Test Store ve production anahtarlarini her zaman ayri tutun:

kotlin
object RevenueCatKeys {
    // Test Store - for testing only
    const val TEST_STORE_KEY = "test_xxxxx"

    // Google Play - for production
    const val GOOGLE_PLAY_KEY = "goog_xxxxx"

    fun getApiKey(isTestMode: Boolean): String {
        return if (isTestMode) TEST_STORE_KEY else GOOGLE_PLAY_KEY
    }
}

Build Variant kurulum

Build variant'lari kullanarak ortamlar arasinda otomatik gecisi uygulayin:

kotlin
android {
    buildTypes {
        debug {
            buildConfigField("String", "RC_API_KEY", "\"test_xxxxx\"")
            buildConfigField("Boolean", "USE_TEST_STORE", "true")
        }

        release {
            buildConfigField("String", "RC_API_KEY", "\"goog_xxxxx\"")
            buildConfigField("Boolean", "USE_TEST_STORE", "false")
        }
    }

    flavorDimensions += "environment"
    productFlavors {
        create("dev") {
            dimension = "environment"
            buildConfigField("String", "RC_API_KEY", "\"test_xxxxx\"")
        }

        create("staging") {
            dimension = "environment"
            buildConfigField("String", "RC_API_KEY", "\"test_xxxxx\"")
        }

        create("prod") {
            dimension = "environment"
            buildConfigField("String", "RC_API_KEY", "\"goog_xxxxx\"")
        }
    }
}

Kapsamli Bir Test Stratejisi Olusturma

Uygulama ici satin almalar icin saglam bir test stratejisi piramit yaklasimini izlemelidir. Test Store kullanarak birim testleriyle baslayin ve satin alma mantgini izole olarak dogrulayin. Bunlar hizli calisir ve cogu sorunu erkenden yakalar. Bu temel uzerine, yine Test Store kullanan entegrasyon testleri olusturarak tam satin alma akisini uctan uca kontrol edin. Uygulamaniza guvendiginizde, platform entegrasyonunu kontrol etmek icin Google Play Sandbox ile manuel teste gecin. Son olarak, canli ortamda her seyin calistigini dogrulamak icin gercek satin almalarla kucuk olcekli production testleri yapin.

Test Store ve Sandbox Arasinda Secim Yapma

Erken gelistirme ve yineleme sirasinda Test Store en iyi seciminizdir. Satin alma akislarinin hizli prototiplenmesi, birim ve entegrasyon testleri yazma, hata isleme ve uc durum testleri icin mukemmeldir. Anlik geri bildirim dongusu, guvenilir ve hizli sonuclar gerektiren CI/CD otomatik testleri icin idealdir.

Test Store'un simule etmedigi platforma ozgu ozellikleri test etmeniz gerektiginde Google Play Sandbox'a gecin. Buna bekleyen islemler (ornegin ebeveyn onayi bekleme), makbuz dogrulama ayrintilari, abonelik yenileme donguleri ve bolgeleye ozgu fiyatlandirma dahildir. Sandbox'i production oncesi dogrulama ortami olarak dusuunun. Temel mantginizin saglam oldugunu bildiginizde ve Google Play entegrasyonunu kontrol etmeniz gerektiginde kullanin.

Guvenlik En Iyi Uygulamalari

Production'da asla Test Store API anahtarini ifsa etmeyin:

kotlin
// ❌ BAD: Hardcoded keys
val apiKey = "test_xxxxx"

// ✅ GOOD: Environment-based configuration
val apiKey = if (BuildConfig.DEBUG) {
    BuildConfig.TEST_STORE_API_KEY
} else {
    BuildConfig.GOOGLE_PLAY_API_KEY
}

// ✅ BETTER: Check for test environment
val apiKey = when {
    isRunningInTests() -> BuildConfig.TEST_STORE_API_KEY
    BuildConfig.DEBUG -> BuildConfig.TEST_STORE_API_KEY
    else -> BuildConfig.GOOGLE_PLAY_API_KEY
}

Loglama ve Hata Ayiklama

Uygun loglama seviyelerini etkinlestirin:

kotlin
if (BuildConfig.USE_TEST_STORE) {
    Purchases.logLevel = LogLevel.VERBOSE
    Log.d("RevenueCat", "Using Test Store for testing")
} else if (BuildConfig.DEBUG) {
    Purchases.logLevel = LogLevel.DEBUG
} else {
    Purchases.logLevel = LogLevel.INFO
}

Production'a Gecis

Uygulamanizi yayinlamadan once kapsamli bir kontrol listesini tamamladiginizdan emin olun. Tum testler Test Store ile gecmeli ve Google Play Sandbox ile entegrasyon manuel olarak dogrulanmalidir. Build variant'larin her ortamda dogru API anahtarini kullanacak sekilde dogru yapilandirildigini tekrar kontrol edin. Production API anahtari guvenli tutulmali ve surum kontrolune commit edilmemelidir. Test Store anahtarinin release build'lerden tamamen kaldirildigini dogrulayin; yalnizca debug yapilandirmasinda bulunmalidir. Loglamanin uygun sekilde ayarlandigini kontrol edin (debug'da ayrintili, production'da minimum). Tum senaryolarda hata islemenin test edildigini dogrulayin.

Sonuç

Tebrikler! Android icin RevenueCat'in Test Store'unu basariyla kurdunuz. Bu codelab'de RevenueCat panelinde Test Store'u etkinlestirmeyi, Android uygulamanizda Test Store API anahtarini yapilandirmayi, belirleyici sonuclarla satin alma akislarini test etmeyi, satin alma mantigi icin otomatik birim testleri yazmayi, GitHub Actions gibi CI/CD ortamlarinda bu testleri calistirmayi ve ortam ayirimi icin en iyi uygulamalari ogrendiniz.

Temel Cikarimlar

Test Store, uygulama ici satin alma testine yaklasim sekilinizi temelden degistirir. Sandbox hesaplariyla ugrasma ve tutarsiz aga bagli testlerle bas etme gunleri geride kaldi. Satin alma sonuclari uzerinde belirleyici kontrol sayesinde uygulamaniza gercek guven veren guvenilir testler yazabilirsiniz. CI/CD entegrasyonu, her kod degisikliginde satin alma mantginin surekli dogrulandigini gosterir ve satin alma akislarini saatler yerine saniyeler icinde yineleyebilirsiniz. En onemlisi, satin almayla ilgili hatalari production'a ulasmadan once yakalayarak hem gelirinizi hem de kullanici deneyimini koruyabilirsiniz.

Sonraki Adimlar

Artik Test Store'u kurdugunuza gore, bu temel uzerine insa etme zamani. Tum satin alma senaryolarini kapsayan kapsamli testler yazarak baslayin; sadece mutlu yolu test etmeyin. Bu testleri CI/CD pipeline'iniza entegre edin, boylece her pull request'te otomatik olarak calisirlar. Uygulamanizin duzgun bir sekilde bozulup bozulmadigini kontrol etmek icin cesitli basarisizlik modlariyla hata islemeyi kapsamli bir sekilde test edin. Hazir oldugunuzda, abonelik yenilemeleri gibi seyleri kontrol etmek icin platforma ozgu testler icin Google Play Sandbox'a gecin. Son olarak, satin alma mantginizin her adimda kapsamli bir sekilde dogrulandigini bilerek guvenle yayinlayin.

Ek Kaynaklar

RevenueCat Test Store Blog Yazisi RevenueCat Android SDK Belgeleri Test Kilavuzu GitHub: Cat Paywall Compose

Iyi testler!