JUnit 5 Temeller
JUnit 5 (diğer adıyla JUnit Jupiter), Java ekosisteminin standart test framework'üdür. Spring Boot 2.2+ ile varsayılan olarak gelir ve güçlü anotasyonlar, esnek assertion'lar ve modern test yazım kalıpları sunar. Bu derste JUnit 5'in tüm temel özelliklerini öğreneceğiz.
JUnit 5 Mimarisi
JUnit 5, üç modülden oluşur:
JUnit Platform: Test çalıştırma altyapısı (IDE ve build tool entegrasyonu)
JUnit Jupiter: JUnit 5'in yeni programlama modeli ve anotasyonları
JUnit Vintage: JUnit 3 ve JUnit 4 testlerini JUnit 5 platformunda çalıştırma desteği
Spring Boot projesinde JUnit 5 otomatik olarak gelir:
<!-- spring-boot-starter-test JUnit 5'i içerir -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>Temel Anotasyonlar
import org.junit.jupiter.api.*;
class CalculatorTest {
private Calculator calculator;
@BeforeAll
static void setupAll() {
// Tüm testlerden ÖNCE bir kez çalışır
// static olmalı — sınıf seviyesinde hazırlık
System.out.println("Test sınıfı başlatılıyor...");
}
@AfterAll
static void tearDownAll() {
// Tüm testlerden SONRA bir kez çalışır
// static olmalı — sınıf seviyesinde temizlik
System.out.println("Test sınıfı tamamlandı.");
}
@BeforeEach
void setUp() {
// Her testten ÖNCE çalışır
// Test izolasyonu için her seferinde yeni nesne oluşturulur
calculator = new Calculator();
}
@AfterEach
void tearDown() {
// Her testten SONRA çalışır
// Kaynak temizliği (dosya kapatma, bağlantı sonlandırma vb.)
calculator = null;
}
@Test
@DisplayName("İki pozitif sayı toplanmalı")
void shouldAddTwoPositiveNumbers() {
assertEquals(5, calculator.add(2, 3));
}
@Test
@DisplayName("Sıfıra bölme exception fırlatmalı")
void shouldThrowOnDivisionByZero() {
assertThrows(ArithmeticException.class,
() -> calculator.divide(10, 0));
}
@Test
@Disabled("Bug #123 düzeltilene kadar devre dışı")
void temporarilyDisabledTest() {
// Bu test çalışmaz ama raporda görünür
}
}Yaşam döngüsü sırası: @BeforeAll → (@BeforeEach → @Test → @AfterEach) × N → @AfterAll
Assertions — Doğrulama Metotları
JUnit 5'in Assertions sınıfı zengin doğrulama metotları sunar:
import static org.junit.jupiter.api.Assertions.*;
class AssertionExamplesTest {
@Test
void basicAssertions() {
// Eşitlik kontrolü
assertEquals(4, 2 + 2);
assertEquals("hello", "hello");
assertEquals(3.14, Math.PI, 0.01); // delta ile double karşılaştırma
// Boolean kontrol
assertTrue(5 > 3);
assertFalse(2 > 5);
// Null kontrol
assertNull(null);
assertNotNull("value");
// Aynı referans kontrolü
String s1 = "test";
String s2 = s1;
assertSame(s1, s2);
// Özel hata mesajı — test başarısız olursa gösterilir
assertEquals(10, calculateTotal(),
"Toplam tutar 10 olmalıydı");
// Lambda ile lazy message — sadece başarısız olursa evaluate edilir
assertEquals(10, calculateTotal(),
() -> "Beklenen: 10, Gerçek: " + calculateTotal());
}
@Test
void assertAllExample() {
// assertAll: TÜMÜNÜ çalıştırır, hangilerinin başarısız olduğunu raporlar
// Normal assert'te ilk başarısızlıkta durur
User user = new User("Ali", "ali@test.com", 25);
assertAll("User doğrulaması",
() -> assertEquals("Ali", user.getName()),
() -> assertEquals("ali@test.com", user.getEmail()),
() -> assertTrue(user.getAge() > 0),
() -> assertNotNull(user.getName())
);
}
@Test
void exceptionAssertions() {
// Exception fırlatıldığını doğrula
Exception ex = assertThrows(IllegalArgumentException.class,
() -> new Product("", -5));
// Exception mesajını da doğrulayabilirsiniz
assertEquals("Fiyat negatif olamaz", ex.getMessage());
assertTrue(ex.getMessage().contains("negatif"));
// Exception fırlatılMADIĞINI doğrula
assertDoesNotThrow(() -> new Product("Laptop", 999));
}
@Test
@Timeout(value = 2, unit = TimeUnit.SECONDS)
void timeoutAssertion() {
// Belirli sürede tamamlanmalı
assertTimeout(Duration.ofMillis(500), () -> {
// 500ms içinde bitmeli
Thread.sleep(100);
});
}
}@Nested — İç İçe Test Sınıfları
@Nested, ilişkili testleri gruplamak için kullanılır. BDD (Behavior-Driven Development) tarzında okunabilir test hiyerarşisi oluşturur:
@DisplayName("UserService Testleri")
class UserServiceTest {
private UserService userService;
private UserRepository mockRepo;
@BeforeEach
void setUp() {
mockRepo = mock(UserRepository.class);
userService = new UserService(mockRepo);
}
@Nested
@DisplayName("createUser metodu")
class CreateUser {
@Test
@DisplayName("geçerli kullanıcı oluşturmalı")
void shouldCreateValidUser() {
User user = new User("Ali", "ali@test.com");
when(mockRepo.save(any())).thenReturn(user);
User result = userService.createUser("Ali", "ali@test.com");
assertNotNull(result);
assertEquals("Ali", result.getName());
}
@Test
@DisplayName("boş isim verilince hata fırlatmalı")
void shouldThrowWhenNameIsEmpty() {
assertThrows(IllegalArgumentException.class,
() -> userService.createUser("", "ali@test.com"));
}
@Nested
@DisplayName("email doğrulama")
class EmailValidation {
@Test
@DisplayName("geçersiz email formatta hata fırlatmalı")
void shouldThrowForInvalidEmail() {
assertThrows(IllegalArgumentException.class,
() -> userService.createUser("Ali", "invalid"));
}
}
}
@Nested
@DisplayName("findUser metodu")
class FindUser {
@Test
@DisplayName("var olan kullanıcıyı bulmalı")
void shouldFindExistingUser() {
when(mockRepo.findById(1L))
.thenReturn(Optional.of(new User("Ali", "ali@test.com")));
Optional<User> result = userService.findById(1L);
assertTrue(result.isPresent());
}
}
}Test raporu çıktısı:
UserService Testleri
├─ createUser metodu
│ ├─ ✓ geçerli kullanıcı oluşturmalı
│ ├─ ✓ boş isim verilince hata fırlatmalı
│ └─ email doğrulama
│ └─ ✓ geçersiz email formatta hata fırlatmalı
└─ findUser metodu
└─ ✓ var olan kullanıcıyı bulmalı@ParameterizedTest — Parametreli Testler
Aynı test mantığını farklı verilerle tekrar tekrar çalıştırmak için kullanılır:
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
class ParameterizedExamplesTest {
// @ValueSource — basit değer listesi
@ParameterizedTest(name = "{0} pozitif olmalı")
@ValueSource(ints = {1, 5, 100, Integer.MAX_VALUE})
void shouldBePositive(int number) {
assertTrue(number > 0);
}
// @NullAndEmptySource — null ve boş string testi
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {" ", "\t", "\n"})
void shouldRejectBlankStrings(String input) {
assertThrows(IllegalArgumentException.class,
() -> validator.validateName(input));
}
// @CsvSource — virgülle ayrılmış çoklu parametre
@ParameterizedTest(name = "{0} + {1} = {2}")
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"10, -5, 5",
"0, 0, 0",
"-1, -1, -2"
})
void shouldAdd(int a, int b, int expected) {
assertEquals(expected, calculator.add(a, b));
}
// @EnumSource — enum değerleri ile test
@ParameterizedTest
@EnumSource(value = DayOfWeek.class, names = {"SATURDAY", "SUNDAY"})
void shouldBeWeekend(DayOfWeek day) {
assertTrue(calendarService.isWeekend(day));
}
// @MethodSource — metottan veri sağlama
@ParameterizedTest
@MethodSource("provideUsersForValidation")
void shouldValidateUser(String name, String email, boolean expected) {
assertEquals(expected, validator.isValid(name, email));
}
static Stream<Arguments> provideUsersForValidation() {
return Stream.of(
Arguments.of("Ali", "ali@test.com", true),
Arguments.of("", "ali@test.com", false),
Arguments.of("Ali", "invalid", false),
Arguments.of(null, "ali@test.com", false)
);
}
}Test Lifecycle ve Instance Per Method
JUnit 5 varsayılan olarak her test metodu için yeni bir test sınıfı instance'ı oluşturur. Bu, testler arasında state paylaşımını önler:
class InstancePerMethodTest {
private int counter = 0;
@Test
void firstTest() {
counter++;
assertEquals(1, counter); // her zaman 1 — yeni instance
}
@Test
void secondTest() {
counter++;
assertEquals(1, counter); // yine 1 — farklı instance
}
}
// İsterseniz davranışı değiştirebilirsiniz (önerilmez):
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SharedInstanceTest {
// Tek instance — testler arasında state paylaşılır
// @BeforeAll ve @AfterAll static olmak zorunda değil
}JUnit 5, modern Java test yazımının temelidir. @DisplayName ile okunabilir isimler, @Nested ile hiyerarşik yapı, @ParameterizedTest ile veri-güdümlü testler ve zengin assertion kütüphanesi sayesinde kapsamlı ve bakımı kolay test suitleri oluşturabilirsiniz.
AI Asistan
Sorularını yanıtlamaya hazır