Birim testi, sistemin geri kalanından yalıtılmış olarak tek tek modüllerin veya kod işlevlerinin doğruluğunu test eden bir yazılım doğrulama yöntemidir. Martin Fowler, 2026'ya göre, birim testler CI/CD ve yeniden düzenlemenin temelidir ve kodun çalışabilirliği hakkında hızlı geri bildirim sağlar. Modül testi, geliştirmenin erken aşamalarında hataları tespit etmeye yardımcı olur ve düzeltme maliyetini kat kat azaltır.
Önemli Noktalar
Birim testi, kaynak kodun bireysel birimlerinin (işlevler, yöntemler, sınıflar) programın geri kalanından yalıtılarak doğrulanması sürecidir. Her test, modülün belirli bir kullanım senaryosunu çalıştırır ve sonucun beklenenle eşleşip eşleşmediğini kontrol eder. Birim testler, ana kodla aynı programlama dilinde yazılır ve geliştirme ortamında veya CI/CD hattında otomatik olarak çalıştırılır. Entegrasyon testlerinin aksine, birim testler gerçek veritabanları, dosya sistemleri veya ağ hizmetleriyle etkileşime girmez.
Temel amaç, değişikliklerden sonra kodun doğruluğu hakkında hızlı geri bildirim sağlamaktır. Bir geliştirici bir yöntemi yeniden düzenlerse, birim test paketi davranışın bozulmadığını onaylar. Google Testing Blog (2025)'e göre, birim test kapsamı %60'ın üzerinde olan projelerde üretim olayları 2,5 kat daha az görülür. Ek faydalar: kod dokümantasyonu (testler API'nin nasıl kullanılacağını gösterir), yeniden düzenlemenin basitleştirilmesi (davranışı korurken uygulama değiştirilebilir) ve hızlı regresyon teşhisi.
Her otomatik test bir birim testi değildir. Kriterler: tek bir modül (sınıf veya işlev) test edilir, harici bağımlılıklar mock veya stub ile değiştirilir, test milisaniyeler içinde çalışır ve bir sunucu veya veritabanı başlatılmasını gerektirmez. Gerçek bir veritabanına erişen test bir entegrasyon testidir. Tarayıcı açan bir test E2E testidir. Test türleri arasındaki sınırları anlamak, test piramidinde çabaların doğru dağıtılması için önemlidir.
Kaliteli birim testler, Robert C. Martin tarafından formüle edilen FIRST ilkelerini takip eder. Her test Fast (hızlı — milisaniye), Isolated (yalıtılmış — diğer testlere bağlı değil), Repeatable (tekrarlanabilir — her makinede aynı sonuç), Self-validating (kendi kendini doğrulayan — sonuç "geçti" veya "kaldı", manuel kontrol yok) ve Timely (zamanında — koddan önce veya aynı anda yazılmış) olmalıdır. Herhangi bir ilkeyi ihlal etmek testin değerini azaltır.
Birim testleri yazmak için standart bir şablon. Arrange — veri ve bağımlılıkların hazırlanması: nesneler oluşturma, mock'ları yapılandırma, giriş parametrelerini ayarlama. Act — test edilen eylemin gerçekleştirilmesi: bir yöntem veya işlev çağırma. Assert — sonucun doğrulanması: gerçek değerin beklenen değerle karşılaştırılması. Üç bloğa bölme, testi okunabilir ve anlaşılır kılar. Assert bloğu karmaşık mantık gerektiriyorsa, test muhtemelen aynı anda çok fazla şeyi kontrol ediyordur.
// Kotlin'de JUnit 5 ile AAA desenini kullanan birim testi örneği
class CalculatorTest {
private lateinit var calculator: Calculator
@BeforeEach
fun setUp() {
// ARRANGE — test nesnesini oluştur
calculator = Calculator()
}
@Test
fun addition_shouldReturnCorrectSum() {
// ACT — eylemi gerçekleştir
val result = calculator.add(2, 3)
// ASSERT — sonucu doğrula
Assertions.assertEquals(5, result)
}
}
Test adı, neyin test edildiğini ve hangi sonucun beklendiğini açıklamalıdır. Biçim: [methodName]_[scenario]_[expectedResult]. Örnek: calculateTotal_whenDiscountApplied_shouldReturnDiscountedTotal. İyi bir test adı, bir yorumun yerini alır ve başarısız olduğunda hangi işlevselliğin bozulduğunu hemen gösterir. test1, checkSomething veya verify gibi adlardan kaçının — bilgi taşımazlar ve teşhisi zorlaştırırlar.
Test edilen modülü harici bağımlılıklardan yalıtmak için test çiftleri (test doubles) kullanılır. Ana türler: mock'lar — belirli bir yöntemin beklenen parametrelerle çağrıldığını doğrular; stub'lar — bir yöntem çağrıldığında önceden tanımlanmış değerler döndürür; fake'ler — gerçek bileşenlerin basitleştirilmiş uygulamaları (örneğin, bir veritabanıyla çalışan UserRepository yerine InMemoryUserRepository). Seçim, neyin doğrulanması gerektiğine bağlıdır: durum (stub) veya etkileşim (mock).
| Çift | Ne doğrular | Örnek |
|---|---|---|
| Mock | Doğru parametrelerle yöntem çağrısı | userRepository.save(user) tam olarak 1 kez çağrıldı |
| Stub | Dönüş değeri | repository.findById(1) User(id=1, name="Test") döndürür |
| Fake | Basitleştirilmiş uygulama yoluyla mantık | DB yerine HashMap ile InMemoryMapUserRepository |
| Spy | Gerçek bir nesnenin kısmi mock'lanması | spy(repo).when(findById).thenReturn(user) |
Mockito, Java ve Kotlin için en popüler mock'lama çerçevesidir. mock() ile mock oluşturma, when().thenReturn() ile dönüş değerlerini yapılandırma ve verify() ile çağrıları doğrulama imkanı sağlar. Mockito'nun (5.x) modern sürümleri, statik mock'ları (mockStatic) ve BDDMockito (given-willReturn) aracılığıyla basitleştirilmiş sözdizimini destekler. Önemli bir kural: size ait olmayanı mock'lamayın — değer nesneleri ve standart kütüphaneler için mock oluşturmayın.
// Kotlin'de Mockito ile birim testi örneği
class OrderServiceTest {
@Mock
private lateinit var paymentGateway: PaymentGateway
@Mock
private lateinit var userRepository: UserRepository
private lateinit var orderService: OrderService
@BeforeEach
fun init() {
MockitoAnnotations.openMocks(this)
orderService = OrderService(paymentGateway, userRepository)
}
@Test
fun processOrder_whenPaymentFails_shouldThrowException() {
// given
val user = User(id = 1, balance = 100.0)
val order = Order(amount = 200.0)
Mockito.`when`(paymentGateway.charge(any())).thenReturn(false)
Mockito.`when`(userRepository.findById(1)).thenReturn(user)
// when & then
assert Throws<PaymentException> {
orderService.processOrder(user.id, order)
}
// verify
Mockito.verify(paymentGateway).charge(any())
}
}
TDD (Test-Driven Development), kod uygulamasından önce testin yazıldığı bir metodolojidir. "Kırmızı-Yeşil-Yeniden Düzenleme" döngüsü: başarısız olan bir test yazın (Kırmızı), testi geçmek için minimum kodu yazın (Yeşil), davranışı değiştirmeden kodu iyileştirin (Yeniden Düzenleme). TDD, tüm kodun testlerle kapsandığını (uygulanan işlevsellik için kapsam = %100) ve kodun test edilebilir olduğunu garanti eder — kod test etmesi zorsa, mimarinin iyileştirilmesi gerekir.
IBM (2006-2026, boylamsal çalışma) araştırmasına göre, TDD kullanan ekipler, testleri koddan sonra yazan ekiplere kıyasla üretimde %40-80 daha az hata yapar. TDD ayrıca mimariyi iyileştirir: geliştirici, uygulamadan önce API tasarımı hakkında düşünmek zorunda kalır ve bu da gevşek bağlantı (loose coupling) ve yüksek uyum (high cohesion) sağlar. Ek bir etki de canlı dokümantasyondur: testler, her zaman güncel olan modül davranışının bir spesifikasyonu olarak hizmet eder.
TDD her zaman optimal değildir. UI bileşenlerini yalıtarak test etmek zordur — onlar için anlık görüntü testleri veya görsel regresyon testleri (Percy, Chromatic) daha etkilidir. Prototipleme ve araştırma (spike çözümler) test gerektirmez. Testleri olmayan eski kodu TDD ile kapsamak zordur — burada önce karakterizasyon testleri (yeniden düzenlemeden önce mevcut davranışı yakalayan testler) gereklidir. Bu durumlarda, TDD tamamen terk edilmez, uyarlanır — tüm eski kod için değil, değiştirilen işlevsellik için testler yazılır.
Mobil geliştirmenin kendine özgü yönleri vardır: iş mantığı genellikle UI koduyla (Activity, ViewController, ViewModel) karışır ve bu da birim testini zorlaştırır. En iyi uygulama ince View'lar, kalın ViewModel'lerdir: tüm mantığı UI bileşenlerinden ayrı sınıflara (UseCase, Repository, ViewModel) çıkarın ve bunlar öykünücü olmadan kolayca test edilebilir. Android ve iOS, bir cihaz başlatmadan JVM/Native üzerinde çalışan yerel birim test çerçevelerine sahiptir.
Android birim testleri öykünücü olmadan yerel JVM'de çalışır ve yürütme hızı sağlar — tipik bir test 100ms'den kısa sürer. JUnit 5 ana çalıştırıcıdır. ViewModel testleri için, eşyordam testi için kotlinx-coroutines-test ve StateFlow testi için Turbine kullanın. Robolectric, gölge sınıfları yükleyerek öykünücü olmadan Android bağımlı bileşenleri (Context, Resources) test etmeye olanak tanır. Compose testleri için Compose UI Test kullanın — ancak bunlar UI testleridir, birim testleri değil.
iOS birim testleri, XCTest (Xcode'da yerleşik) ile Swift'te yazılır. Quick + Nimble, daha okunabilir testler (describe/context/it) için BDD çerçeveleridir. Mock'lama için Cuckoo (mock oluşturma) veya SwiftyMocky kullanın. Swift, protokolleri ve bağımlılık enjeksiyonunu destekleyerek bağımlılıkların değiştirilmesini kolaylaştırır. Kilit nokta: iOS birim testleri gerçek bir cihazda değil, macOS simülatöründe çalışır. Donanım özellikleri (kamera, Bluetooth) gerektiren testler entegrasyon testleridir.
Flutter birim testleri flutter_test paketini kullanır ve öykünücü olmadan Dart VM'de çalışır. Mock'lama için, kod oluşturma (build_runner) ile birlikte mockito paketini kullanın. Widget testleri (aynı pakette) bireysel widget'ları test eder ancak işleme gerektirir ve daha yavaştır — bunları yalnızca UI mantığı doğrulaması için kullanın. Saf Dart mantığı (modeller, depolar, bloklar) flutter_test içe aktarılmadan normal Dart testleri olarak test edilir.
// Flutter'da mockito ile birim testi örneği
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([ApiClient])
import 'user_repository_test.mocks.dart';
void main() {
late MockApiClient mockApi;
late UserRepository repository;
setUp(() {
mockApi = MockApiClient();
repository = UserRepository(mockApi);
});
test('fetchUser returns user when API succeeds', () async {
// Arrange
final expectedUser = User(id: 1, name: 'Test');
when(mockApi.getUser(1))
.thenAnswer((_) async => expectedUser);
// Act
final result = await repository.fetchUser(1);
// Assert
expect(result, expectedUser);
verify(mockApi.getUser(1)).called(1);
});
}
Etkili birim testi disiplin gerektirir. Ana kural: davranışı test edin, uygulamayı değil. Test, modülün dahili olarak nasıl uygulandığını (hangi özel yöntemlerin hangi sırayla çağrıldığını) bilmemelidir. Bir test uygulamaya bağlıysa, her yeniden düzenlemede kırılır ve değerini kaybeder. Test sözleşmeyi doğrular: X girdisinde, Y çıktısı olmalıdır. İstisna, çağrı sırasının önemli olduğu kritik performansa sahip algoritmaların testleridir.
%100 kapsam ulaşılamaz ve gereksiz bir hedeftir. Google Testing Blog (2025)'e göre, birim testleri için optimal kapsam seviyesi kod satırlarının %70-80'idir. %100 kapsam genellikle getter, setter ve yapıcıları test ederek elde edilir ve bu değer katmaz. Kritik iş mantığına odaklanın: karmaşık hesaplamalar, doğrulama, hata işleme, uç durumlar. Ölçüm için JaCoCo (Java), Coverage.py (Python), Istanbul (JS) kullanın ve CI'da bir eşik belirleyin — kapsam %60'ın altındayken derleme hatası.
Birim testleri, herhangi bir CI/CD hattının ilk aşamasıdır. Derleme ve dağıtımdan önce, depoya her push'ta çalıştırılırlar. Ortalama birim test paketi çalışma süresi 5 dakikayı geçmemelidir — daha uzun olursa, testler "hızlı" olmaktan çıkar ve geliştiriciler onları yerel olarak çalıştırmayı bırakır. Testleri hızlı (birim) ve yavaş (entegrasyon) olarak ayırın ve hattın farklı aşamalarında çalıştırın. Hız için paralel yürütme ve hızlı başarısızlık kullanın.
Sıkça Sorulan Sorular
Birim testi, harici bağımlılıkları mock'larla değiştirerek tek bir modülü yalıtarak doğrular. Entegrasyon testi, birden çok gerçek bileşen (DB, API, dosya sistemi) arasındaki etkileşimi doğrular. Birim testler milisaniyeler içinde, entegrasyon testleri saniyeler içinde çalışır. Test piramidinde birim testler %70'i oluşturur.
Seçim platforma bağlıdır: Java/Kotlin için JUnit 5, iOS/Swift için XCTest, Python için pytest, JavaScript/TypeScript için Jest/Vitest, Flutter için flutter_test. Mock'lama için Mockito (Java), Cuckoo (iOS), unittest.mock (Python) veya vitest.mock (JS) kullanın. Tüm modern çerçeveler parametreli testleri, yerleşik iddiaları ve paralel yürütmeyi destekler.
Fast — test milisaniyeler içinde çalışır. Isolated — diğer testlere veya harici sistemlere bağlı değildir. Repeatable — her makinede aynı sonucu verir. Self-validating — sonucu otomatik olarak doğrular. Timely — koddan önce veya aynı anda yazılır. Tek bir ilkenin ihlali bile test etkinliğini azaltır.
Evet, kesinlikle. ViewModel iş mantığı içerir — olay işleme, veri dönüşümü, durum yönetimi. Android'de, eşyordamlar için kotlinx-coroutines-test ve StateFlow testi için Turbine kullanın. iOS'ta, ViewModel'de Combine Publishers veya async/await test edin. ViewModel testleri, öykünücü olmadan JVM/macOS'te çalışan saf birim testleridir.
Birim testlerinde ağ istekleri yürütülmez — HTTP istemci mock'larıyla değiştirilir. Android'de MockWebServer (OkHttp) kullanın — yerel bir HTTP sunucusu başlatır, gerçek ağ etkileşimini yeniden ürettiği için mock'lardan daha iyidir. MockWebServer, gerçekçiliği kaybetmeden yalıtım sağlar. iOS için — yanıtları kesmek ve değiştirmek için OHHTTPStubs veya URLProtocol kullanın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun