JAVA UNIT TEST NEDİR?
JUnit 4 ile yazılmış eski bir test sınıfını mı sürdürmeli, yoksa baştan JUnit 5 (Jupiter) ile mi kurmalı? İki sürüm de aynı amaca hizmet ediyor gibi görünür: bir metodun beklediğiniz çıktıyı verip vermediğini doğrulamak. Ama annotation isimleri, lifecycle davranışı, extension modeli ve parametreli test yazma şekli o kadar farklılaşmıştır ki, aynı test mantığını her iki tarafa taşımak çoğu zaman birebir kopyala-yapıştır olmaz. Bu yazıda Java tarafında unit test kavramının ne anlama geldiğini ve JUnit 4 ile JUnit 5'in pratikte nerelerde ayrıştığını somut örneklerle inceliyoruz.
Unit Test Java Dünyasında Ne Anlatır?
Unit test, bir sınıfın tek bir davranışını — genellikle public bir metodun belirli bir input için ürettiği output'u — izole biçimde doğrulayan koddur. "İzole" kelimesi kritik: veritabanı, ağ, dosya sistemi gibi dış bağımlılıklar test sırasında ya mock'lanır ya da test edilen birimden uzak tutulur. Amaç, hata çıktığında nedenini sadece o sınıfın içinde aramak.
Java tarafında bu pratiğin de-facto kütüphanesi JUnit'tir. JUnit 4, 2006'dan itibaren ekosistemin omurgası oldu; JUnit 5 ise 2017'de modüler bir mimariyle (Platform + Jupiter + Vintage) yeniden yazıldı. İkisi de Maven veya Gradle ile kolayca projeye eklenebilir, IDE entegrasyonu sorunsuz çalışır.
Annotation İsimleri: Aynı Niyet, Farklı Etiketler
İki sürüm arasındaki en görünür fark annotation isimlerindedir. Lifecycle annotation'ları doğrudan aynı işi yapar ama farklı paketlerden gelir ve farklı adlar taşır:
@Before(JUnit 4) →@BeforeEach(JUnit 5)@After(JUnit 4) →@AfterEach(JUnit 5)@BeforeClass(JUnit 4) →@BeforeAll(JUnit 5)@AfterClass(JUnit 4) →@AfterAll(JUnit 5)@Ignore(JUnit 4) →@Disabled(JUnit 5)@Test(expected = ...)(JUnit 4) →assertThrows(...)(JUnit 5)
JUnit 4'te @Test import'u org.junit.Test'tir; JUnit 5'te ise org.junit.jupiter.api.Test. Aynı sınıfta yanlış import yapıldığında test runner sessizce metodu atlar — bu, sürüm geçişinde en sık yapılan hatadır. Ayrıca JUnit 5'te @DisplayName ile test isimlerine boşluklu, Türkçe karakterli açıklayıcı isimler verilebilir; JUnit 4'te bu yetenek yoktur.

Runner'dan Extension Modeline Geçiş
JUnit 4'te test sınıfının davranışını özelleştirmek için @RunWith annotation'u kullanılır ve bir sınıfa tek bir runner atanır. Mockito için @RunWith(MockitoJUnitRunner.class), Spring için @RunWith(SpringJUnit4ClassRunner.class) yazarsınız. Sorun şudur: bir sınıf aynı anda yalnızca bir runner kullanabilir. İki davranışı birleştirmek için Rule mekanizmasına başvurmak gerekir ve bu da ek karmaşa demektir.
JUnit 5 bu kısıtı tamamen kaldırır. @ExtendWith annotation'u sayesinde bir sınıfa istediğiniz kadar extension takabilirsiniz:
@ExtendWith(MockitoExtension.class)ile Mockito entegrasyonu@ExtendWith(SpringExtension.class)ile Spring context- Aynı sınıfta birden fazla
@ExtendWithbir arada çalışabilir - Kendi yazdığınız extension'lar lifecycle olaylarına (before/after, parameter resolution, exception handling) doğrudan bağlanır
Extension API, runner ve rule'dan çok daha temiz bir kontrat sunar. Hangi annotation'u kullanırsanız kullanın, çağrı sırası ve davranış tahmin edilebilirdir. Java tarafında test otomasyonu pratiklerini uçtan uca öğrenmek için Java test otomasyonu eğitimi içeriğinden yararlanabilirsiniz.
Parametreli Test: 4 Tarafında Eziyet, 5 Tarafında Standart
Aynı test mantığını farklı veri setleriyle çalıştırmak istediğinizde JUnit 4 ile JUnit 5 arasındaki uçurum belirginleşir. JUnit 4'te bunun için sınıf seviyesinde @RunWith(Parameterized.class) kullanmak ve @Parameters ile bir Collection<Object[]> döndüren static metot yazmak zorundasınız. Sınıf yalnızca parametreli testler için ayrılır; aynı dosyada parametresiz bir @Test metodu yazmak istediğinizde projeyi yeniden düzenlersiniz.
JUnit 5 bunu metot seviyesine indirir. @ParameterizedTest annotation'u, kaynak annotation'larıyla beraber kullanılır:
@ValueSource(ints = {1, 2, 3})— tek tip parametreler için@CsvSource({"1, bir", "2, iki"})— birden fazla parametre, satır satır@MethodSource("dataProvider")— kompleks objeler için@EnumSource(MyEnum.class)— enum değerleri için@CsvFileSource(resources = "/data.csv")— harici CSV dosyası
Aynı sınıfta hem parametreli hem parametresiz testler kolayca bir arada yaşar. Bu, test dosyalarını veri setine göre değil, davranışa göre organize etme imkânı verir.
Assertion ve Assumption Davranışı
JUnit 4'te assertion'lar org.junit.Assert sınıfında toplanırken JUnit 5 bunları org.junit.jupiter.api.Assertions içine taşır ve genişletir. Önemli fark, assertAll metodudur: birden fazla assertion'ı tek bir blokta toplar ve ilk hata test'i durdurmaz — hepsi çalışır, hangi assertion'ların başarısız olduğunu topluca raporlar. JUnit 4'te ilk başarısız assertion test'i bitirir, sonrakileri göremezsiniz.
Beklenen exception'lar için JUnit 4'te @Test(expected = X.class) yaklaşımı kullanılır ama hangi satırın exception attığını ölçemezsiniz — testin herhangi bir yerinden gelebilir. JUnit 5'te assertThrows(X.class, () -> methodCall()) ile sadece o lambda'nın exception atması beklenir; gerçekten beklediğiniz yerden geldiğini doğrulamış olursunuz. Üstelik dönen exception nesnesi üzerinden message kontrolü de yapabilirsiniz.

Vintage Engine: Geçişte Köprü
Mevcut bir projeyi JUnit 4'ten JUnit 5'e taşımak ikili bir karar değildir. JUnit 5'in Vintage engine'i, eski @RunWith, @Before, org.junit.Test kullanan test sınıflarını JUnit 5 platformu üzerinde çalıştırır. Yani Maven pom'una hem junit-jupiter hem de junit-vintage-engine ekleyerek aynı build içinde her iki sürümün testlerini yan yana çalıştırabilir, yeni yazdıklarınızı Jupiter ile, eski olanları olduğu gibi bırakabilirsiniz. Bu kademeli geçiş, büyük test tabanını riske atmadan modernleşmenin yoludur. Engine konfigürasyonu, extension yazımı ve lifecycle ayrıntıları için resmi kullanıcı rehberine başvurabilirsiniz.
JUnit 5'in modüler yapısını ve gerçek projelerde nasıl yapılandırıldığını öğrenmek için test otomasyonu eğitim içeriklerini inceleyebilirsiniz. Geçiş kararı verirken ekibin Java sürümü (Jupiter Java 8+ ister), kullanılan ek kütüphanelerin Jupiter desteği ve mevcut CI altyapısı belirleyici olur.
Hangi Sürümle Başlamalı?
Yeni bir Java projesinde tartışmasız olarak JUnit 5 tercih edilir. Daha esnek extension modeli, metot seviyesi parametreli test, @DisplayName ile okunabilir raporlar ve nested test sınıfları (@Nested) ile testleri konuya göre gruplayabilme imkânı, başlangıçta bile somut fayda sağlar. JUnit 4'ü ancak yıllardır süren bir kod tabanına dokunuyorsanız ve risk almak istemiyorsanız sürdürmek mantıklıdır — onun dışında geçiş için zaman ayırmak uzun vadede daha az teknik borç bırakır.



