Tue Aug 12 2025
Merhaba tekrar! React Native geliştirirken çoğumuzun yolu jest ile unit test yazmaya çıkar. Jest süper bir araç, ama genelde sadece küçük parçaları test ediyoruz, tüm uygulamanın gerçek dünyada nasıl çalıştığını görmek zor oluyor. Peki ya kullanıcı deneyimini, ekranlar arası akışı, gerçek etkileşimleri test etmek?
İşte bu noktada Detox devreye giriyor. Detox ile sadece küçük birim değil, uygulamanın tamamını gerçek cihaz veya emülatörde otomatik testlerle sınayabilirsin. Bu sayede hataları erkenden yakalar, kullanıcıların yaşayacağı sürprizleri önlersin.
Bu yazıda, jest’ten bir adım öteye geçip Detox ile React Native uygulamana uçtan uca testler yazmanın tüm aşamalarını, kurulumdan test senaryolarına kadar pratik ve samimi şekilde anlatacağım. Hazırsan, testlerin gücünü keşfetmeye başlayalım!
React Native ile uygulama geliştirirken, bir yandan kod yazarken diğer yandan yazdığımız kodun gerçekten düzgün çalıştığından emin olmak isteriz. İşte tam bu noktada testler devreye girer. Pek çoğumuz jest ile birim test (unit test) yaparız; küçük fonksiyonların, komponentlerin doğru çalışıp çalışmadığını kontrol ederiz. Ancak gerçek kullanıcı deneyimini test etmek jest ile mümkün değil.
Burada Detox devreye giriyor.
Detox, React Native uygulamalar için özel geliştirilmiş, gerçek cihazlar veya simülatörlerde çalışan uçtan uca (End-to-End, E2E) test otomasyon çerçevesidir. Yani, uygulamanın tamamını bir kullanıcının gözünden test etmeni sağlar.
Peki Detox’un farkı ne?
Detox’u gerçekten çok seviyorum çünkü uygulamanı gerçek cihazlarda ya da emülatörlerde açıyor ve tam bir kullanıcı gibi davranıyor: dokunuyor, kaydırıyor, yazıyor… Yani “Gerçek hayatta kullanıcı ne yapıyorsa, Detox da aynısını yapıyor” diyebilirim.
Bir başka güzel yanı da testlerin uygulama ile senkronize çalışması. Mesela “Butona bastım, ekran açıldı mı?” diye düşünmene gerek kalmıyor; Detox sana bu sorunun net cevabını veriyor. Böylece testlerin her zaman tutarlı ve güvenilir oluyor, sonuçlara güvenmek çok daha kolay.
Üstelik testleri komut satırından ya da otomatikleşmiş sistemlerden (CI/CD) kolayca çalıştırabiliyorum. Yani “Aman testleri tekrar tekrar manuel yapmak zorundayım” derdi tamamen ortadan kalkıyor. Jest ile birlikte kullandığında da geliştirici olarak işin gerçekten çok kolaylaşıyor.
Kısacası Detox, uygulamanın “kullanıcı gibi davranan robotu” gibi düşünebilirsin. Test senaryolarını yazıyorsun, Detox ise gerçek bir insanmış gibi adım adım uyguluyor ve sonunda “Her şey yolunda mı?” diye sana güvenilir bir rapor veriyor.
Teorik bilgiler güzel ama en iyisi pratik yapmak, değil mi? Şimdi beraber basit ama gerçekçi bir React Native projesi üzerinden Detox ile uçtan uca test yazmayı öğreneceğiz.
Bu proje, içinde login olma, listeleme ve yeni kayıt ekleme gibi temel özellikler barındırıyor. Böylece, gerçek hayatta karşılaşacağın kullanıcı akışlarını test etmiş olacağız.
Adım adım ilerleyeceğiz; ne yapacağımızı, neden yaptığımızı anlayacaksın. Her satır kodun arkasındaki mantığı kavrayarak, kendi projende nasıl uygulayacağını göreceksin.
Hazırsan, başlayalım! 🚀
Bu rehberde, Detox ile uçtan uca testler yazarken kullanacağımız örnek projeyi de seninle paylaşıyorum. Projemiz oldukça sade ve temel işlevleri içeriyor; böylece testlere odaklanmak kolay oluyor.
Projede neler var?
Bu temel ekranlar, gerçek bir uygulamanın kritik akışlarını simüle ediyor. Böylece Detox testlerini yazarken, gerçek kullanıcı etkileşimlerini taklit edebiliyoruz.
Projeyi Expo ile başlatıp, ardından Bare Workflow’a prebuild aşaması ile geçiş yapacağız. Böylece hem Expo’nun kolaylığından faydalanmış, hem de Detox’un gerektirdiği native yapı desteğini sağlamış olacağız.
npx create-expo-app MyDetoxProject --template blank-typescript
cd MyDetoxProject
🚨 Önemli: Detox ile çalışmadan önce Expo projesini prebuild yapmayı unutmayın.
Bu işlem, native iOS ve Android proje dosyalarını oluşturur.
Neden prebuild? Expo Managed projelerde native dosyalar yoktur, Detox ancak native projelerle çalışır. npx expo prebuild komutuyla native iOS ve Android dosyaları oluşturulur, böylece Detox testleri yapılabilir.
npx expo prebuild
//ios
yarn ios
//android
yarn android
MyDetoxProject/
├── src/
│ ├── screens/
│ │ ├── LoginScreen.tsx
│ │ ├── ListScreen.tsx
│ │ └── AddItemScreen.tsx
│ ├── components/
│ │ └── ... (isteğe bağlı)
│ └── navigation/
│ └── AppNavigator.tsx
├── App.tsx
├── package.json
├── tsconfig.json
└── ... diğer config dosyaları
Projede Detox ile test yazmak için temel ve kritik kod parçalarını aşağıda paylaşıyorum.
Detaylı tüm kodlar ve proje için GitHub reposunu inceleyebilirsiniz.
💡 Not: Detox’un elementleri bulabilmesi için bileşenlere
testID
eklemek gerekir.
Örneğin:<TextInput testID="username-input" />
testID
olmadan da bazı elementler bulunabilir, ancak bu hem kırılgan olur hem de test yazmayı zorlaştırır.
Kararlı ve okunabilir testler içintestID
kullanmak en güvenli yoldur.
//navigation/AppNavigator.tsx
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="List">
{(props) => <ListScreen {...props} items={items} />}
</Stack.Screen>
<Stack.Screen name="Add">
{(props) => <AddScreen {...props} setItems={setItems} />}
</Stack.Screen>
</Stack.Navigator>
//screens/AddItemScreen.tsx
<View style={styles.container}>
<Text style={styles.title}>Yeni Kayıt Ekle</Text>
<TextInput
style={styles.input}
placeholder="Başlık"
value={title}
onChangeText={setTitle}
testID="title-input"
/>
<TextInput
style={[styles.input, { height: 80 }]}
placeholder="Açıklama"
value={desc}
onChangeText={setDesc}
multiline
testID="description-input"
/>
<Picker
selectedValue={category}
onValueChange={(itemValue) => setCategory(itemValue)}
testID="category-picker"
>
<Picker.Item label="Genel" value="Genel" />
<Picker.Item label="İş" value="İş" />
<Picker.Item label="Kişisel" value="Kişisel" />
</Picker>
<Button title="Kaydet" onPress={handleAdd} testID="save-button" />
</View>
//screens/ListScreen.tsx
<View style={styles.container}>
<Text style={styles.title}>Kayıtlar</Text>
<TextInput
style={styles.input}
placeholder="Ara..."
value={search}
onChangeText={setSearch}
testID="search-input"
/>
<FlatList
data={filteredItems}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => (
<View style={styles.card}>
<Text style={styles.cardTitle}>{item.title}</Text>
<Text style={styles.cardDesc}>{item.description}</Text>
<Text style={styles.cardCategory}>{item.category}</Text>
</View>
)}
ListEmptyComponent={<Text>Kayıt yok</Text>}
/>
<Button
title="Yeni Ekle"
onPress={() => navigation.navigate("Add")}
testID="go-add-button"
/>
</View>
//screens/LoginScreen.tsx
<View style={styles.container}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<ScrollView
testID="welcome"
contentContainerStyle={{ flexGrow: 1, justifyContent: "center" }}
>
<Text testID="login-title" style={styles.title}>
Giriş Yap
</Text>
<TextInput
style={styles.input}
placeholder="Kullanıcı Adı"
value={username}
onChangeText={setUsername}
testID="username-input"
/>
<TextInput
style={styles.input}
placeholder="Şifre"
value={password}
onChangeText={setPassword}
secureTextEntry
testID="password-input"
/>
<View style={styles.switchContainer}>
<Text>Beni hatırla</Text>
<Switch
value={remember}
onValueChange={setRemember}
testID="remember-switch"
/>
</View>
<Button title="Giriş" onPress={handleLogin} testID="login-button" />
</ScrollView>
</TouchableWithoutFeedback>
</View>
Artık Testlere Hazırız!
Kodlarımız hazır, proje ayağa kalktı. Şimdi sıra geldi Detox testlerini yazmaya! İlk olarak uygulamayı açacağız, ardından login olacağız. Sonrasında yeni bir kayıt ekleyeceğiz ve gerçekten eklenip eklenmediğini kontrol edeceğiz.
Yani kısaca; Detox ile uygulamanın temel kullanıcı akışını adım adım test edip, her şeyin beklendiği gibi çalıştığını doğrulayacağız. Hazırsan, başlayalım!
Bu Aşamada React Native projemize Detox ile uçtan uca test desteği ekleyeceğiz.
Adımları hem kod hem açıklamalarla ilerleteceğiz.
Test Ortamı Gereklilikleri
- iOS: Güncel Xcode (genelde 13+), macOS ve iOS simülatör gerekiyor.
- Android: Android Studio, uygun SDK (API 30+), ve çalışan AVD emülatörü şart.
# npm ile:
npm install --save-dev detox jest
# yarn ile:
yarn add --dev detox jest
mkdir e2e
e2e/jest.config.js dosyası oluşturup aşağıdaki kodu ekleyin:
// e2e/jest.config.js
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
rootDir: "..",
testMatch: ["<rootDir>/e2e/**/*.test.js"],
testTimeout: 120000,
maxWorkers: 1,
globalSetup: "detox/runners/jest/globalSetup",
globalTeardown: "detox/runners/jest/globalTeardown",
reporters: ["detox/runners/jest/reporter"],
testEnvironment: "detox/runners/jest/testEnvironment",
verbose: true,
};
Bu dosya Jest’in Detox ile birlikte nasıl çalışacağını ayarlıyor. Test konumu, zaman aşımı ve global ayarlar burada belirlenir.
.detoxrc.js dosyasını proje kök dizinine ekleyin:
// .detoxrc.jss
module.exports = {
testRunner: {
args: {
$0: "jest",
config: "e2e/jest.config.js",
},
jest: {
setupTimeout: 120000,
},
},
apps: {
"ios.debug": {
type: "ios.app",
binaryPath:
"ios/build/Build/Products/Debug-iphonesimulator/detoxdemo.app",
build:
"xcodebuild -workspace ios/detoxdemo.xcworkspace -scheme detoxdemo -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
},
"android.debug": {
type: "android.apk",
binaryPath: "android/app/build/outputs/apk/debug/app-debug.apk",
build:
"cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug",
reversePorts: [8081],
},
},
devices: {
simulator: {
type: "ios.simulator",
device: { type: "iPhone 16 Plus" },
},
emulator: {
type: "android.emulator",
device: { avdName: "Pixel_3a_API_30_x86" },
},
},
configurations: {
"ios.sim.debug": {
device: "simulator",
app: "ios.debug",
permissions: { notifications: "YES" },
},
"android.emu.debug": {
device: "emulator",
app: "android.debug",
},
},
};
Burada hangi cihazın kullanılacağı, uygulamanın nasıl build edileceği ve hangi test ortamında çalıştırılacağı ayarlanır.
Not Testleri çalıştırırken, dosya yolları veya uygulama adlarında yapılacak küçük hatalar testlerin başlamasını engeller. Özellikle iOS'ta, uygulama isminde özel karakterler varsa bunlar sorun yaratabilir. Bu nedenle build işleminden sonra dosya ve isimlerin doğruluğunu kontrol etmeyi unutmayın.
//package.json
"scripts": {
...
"detox-ios": "detox test --configuration ios.sim.debug",
"detox-android": "detox test --configuration android.emu.debug",
"detox-build-ios": "detox build --configuration ios.sim.debug",
"detox-build-android": "detox build --configuration android.emu.debug"
}
Testleri kolayca çalıştırmak ve build almak için komutları package.json dosyasına ekliyoruz. Böylece karmaşık Detox komutlarını her seferinde elle yazmak zorunda kalmıyoruz.
Artık e2e klasöründe test dosyaları oluşturup uygulamanızın uçtan uca testlerini yazabilirsiniz.
e2e klasörüne starter.test.js dosyamısı oluşturalım
mkdir e2e/starter.test.js
Bu dosya, uygulamanın temel login akışını, yeni kayıt ekleme işlemini
ve arama filtresi özelliğini test eder.
describe("Login Flow", () => {
// ✅ Testlerden önce uygulamayı sıfırdan başlat
beforeAll(async () => {
await device.launchApp({ delete: true });
});
// 1️⃣ Geçerli bilgilerle giriş yapma testi
it("should login with valid credentials", async () => {
await element(by.id("username-input")).typeText("testuser"); // Kullanıcı adı gir
await element(by.id("password-input")).typeText("123456"); // Şifre gir
await element(by.id("remember-switch")).tap(); // "Beni hatırla" aktif et
await element(by.id("login-title")).tap(); // Klavyeyi kapatmak için başlığa dokun
await element(by.id("login-button")).tap(); // Giriş butonuna tıkla
await expect(element(by.text("Kayıtlar"))).toBeVisible(); // Kayıtlar ekranına geçildi mi kontrol et
});
// 2️⃣ Yeni kayıt ekleme testi
it("should go to add screen and add a new item", async () => {
await element(by.id("go-add-button")).tap(); // "Yeni Ekle" butonuna bas
await element(by.id("title-input")).typeText("Deneme Başlık"); // Başlık gir
await element(by.id("description-input")).typeText("Bu bir açıklamadır"); // Açıklama gir
await element(by.id("save-button")).tap(); // Kaydet butonuna bas
await expect(element(by.text("Deneme Başlık"))).toBeVisible(); // Eklenen başlığı gör
await expect(element(by.text("Genel"))).toBeVisible(); // Kategoriyi gör
});
// 3️⃣ Arama filtresi testi
it("should filter the list by search text", async () => {
await element(by.id("search-input")).typeText("Deneme"); // Arama kutusuna yaz
await expect(element(by.text("Deneme Başlık"))).toBeVisible(); // Filtre sonucu gör
await expect(element(by.text("Genel"))).toBeVisible(); // Kategori de görünsün
await element(by.id("search-input")).replaceText(""); // Arama kutusunu temizle
});
});
Daha fazla detay ve gelişmiş konfigürasyonlar için Detox’un resmi dokümantasyonuna göz atabilirsiniz:
https://wix.github.io/Detox/docs/config/overview
Buradan ileri seviye ayarlar ve kullanım örneklerine ulaşabilirsiniz.
Tamam… Kodları yazdık, testID
’lerimizi ekledik, Detox ayarlarımızı yaptık.
Artık sahne ışıkları açık, seyirciler hazır: Testlerimiz sahneye çıkıyor! 🎭
Uygulamamız sahneye çıkmadan önce hazırlanmalı.
Yani önce build alıyoruz:
iOS için:
npm run detox-build-ios
# veya
yarn detox-build-ios
Android için:
npm run detox-build-android
# veya
yarn detox-build-android
Derleme tamamlandıysa, işte o an geldi… Detox cihazınızı açacak, uygulamanızı başlatacak ve adım adım test senaryolarını oynayacak.
iOS testi:
npm run detox-ios
# veya
yarn detox-ios
Android testi:
npm run detox-android
# veya
yarn detox-android
burak@192 detox-demo % yarn detox-ios
yarn run v1.22.22
$ detox test --configuration ios.sim.debug
23:11:32.128 detox[63281] B jest --config e2e/jest.config.js
23:11:35.752 detox[63282] i starter.test.js is assigned to 5A243533-A563-4CE3-B1D3-D03BFA04929C (iPhone 16 Plus)
23:11:37.463 detox[63282] i com.fivori.detoxdemo launched. To watch simulator logs, run:
/usr/bin/xcrun simctl spawn 5A243533-A563-4CE3-B1D3-D03BFA04929C log stream --level debug --style compact --predicate 'process == "detoxdemo"'
23:11:40.065 detox[63282] i Login Flow: should login with valid credentials
23:11:46.512 detox[63282] i Login Flow: should login with valid credentials [OK]
23:11:46.513 detox[63282] i Login Flow: should go to add screen and add a new item
23:11:53.908 detox[63282] i Login Flow: should go to add screen and add a new item [OK]
23:11:53.909 detox[63282] i Login Flow: should filter the list by search text
23:11:57.900 detox[63282] i Login Flow: should filter the list by search text [OK]
PASS e2e/starter.test.js (25.278 s)
Login Flow
✓ should login with valid credentials (6446 ms)
✓ should go to add screen and add a new item (7394 ms)
✓ should filter the list by search text (3990 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 25.334 s, estimated 26 s
Ran all test suites.
✨ Done in 26.21s.
burak@192 detox-demo %
Test yazmak bazen göz korkutucu olabilir, hele Detox gibi uçtan uca test araçlarıyla çalışmak ilk başta karmaşık görünebilir. Ama inanın bana, bu zorluklar her seferinde biraz daha güven ve başarıyla yer değiştiriyor.
Jest ile birim testler yazmak güzel bir başlangıç, ama Detox ile gerçek kullanıcı deneyimini yakalamak, uygulamanı bir üst seviyeye taşıyor. Bu süreç, sadece kodunun değil, senin de gelişimin için büyük bir adım.
Umarım bu rehber, senin test dünyasında sağlam adımlar atmana yardımcı olur. Unutma; test yazmak sadece hataları bulmak değil, daha kaliteli, sürdürülebilir ve güvenilir bir uygulama inşa etmek demek.
Yeni testler, yeni keşifler ve bol başarılar! Kodlamaya devam et, öğrenmeye devam et, hata yapmaktan korkma ve en önemlisi—bu yolculuğun tadını çıkar. 😊
All rights reserved