Bu yazıda, JUnit ve Mockito kullanarak, yazılım geliştirmenin önemli bir parçası olan unit testlerin bir Spring Boot uygulaması içerisinde nasıl kullanılacağından bahsedeceğim. Bu içerik, unit (birim) test nedir, JUnit ve Mockito nedir, test metotlarının isimlendirme standartları, test annotationları ve unit test uygulamalarını içermektedir.
Unit (Birim) Test Nedir?
Unit Test, uygulamanın içerisindeki belirli işlevlerin sağlıklı olarak gerçekleşip gerçekleşmediğinin kontrol edilmesi için önemlidir. Unit test kavramı, uygulamanın test edilebilir en küçük parçasını kapsar. Bu testler, kodun doğruluğunu sağlamak ve gelecekteki değişikliklerin mevcut kodu bozup bozmadığını belirlemek için kullanılır.
JUnit Nedir?
JUnit, Java tabanlı bir birim test çerçevesidir. Yazılım projelerinde birim testlerin otomatik olarak yürütülmesini, sonuçların analiz edilmesini ve test süreçlerinin yönetilmesini sağlar. Kodun beklenen davranışı sergileyip sergilemediğini doğrulamak amacıyla kullanılır.
Mockito Nedir?
Mockito, gerçek nesnelerin yerine kullanılabilen sahte nesneler (mocks) oluşturarak birim testlerin izole edilmesini sağlar. Bu sayede bir sınıfın bağımlılıklarını test etmek daha kolay hale gelir. Mockito, yazılım geliştirme sürecinde test odaklı yaklaşımların uygulanmasına yardımcı olur ve kodun kalitesini artırır.
Unit Test Annotationları ve Assertions
- @Test: JUnit testlerinin başlangıç noktasını işaretler. Bir test metodu olduğunu belirtir ve bu metotlar JUnit tarafından çalıştırılır.
- @RepeatedTest: Yazılan testi birden fazla çalıştırmak istediğimiz durumlarda kullanılır. Parametre olarak testin çalışma sayısını alır.
- @DisplayName: IDElerin testleri monitör etmeye yarayan sekmelerinde testlerin görüneceği ismi belirlemeye yarar.
@Test @DisplayName("Unsorted List to Sorted List Case") @RepeatedTest(10) public void whenCallSortListWithIntegerList_shouldReturnSortedList() { ... }
Bu örnekte, whenCallSortListWithIntegerList_shouldReturnSortedList fonksiyonunu test olarak işaretledik, IDE’de “Unsorted List to Sorted List Case” olarak görünmesini sağladık ve testin 10 kere çalışmasını istedik.
- @BeforeEach / @BeforeAll: @BeforeAll annotation’ı, test sınıfının tüm test metodları çalışmadan önce bir kez çağrılır. @BeforeEach annotation’ı ise, her test metodu çalışmadan önce çağrılır.
- @AfterEach / @AfterAll: Before annotationlarına benzer şekilde, @AfterAll annotation’ı tüm test metodları çalıştıktan sonra bir kez çağrılır ve @AfterEach annotation’ı her test metodu çalıştıktan sonra çağrılır.
@BeforeEach public void setUp() { listSortService = new ListSortService(); } @AfterEach public void tearDown() { listSortService = null; }
- assertEquals(expected, actual): Beklenen değerle gerçek değeri karşılaştırır. Eğer bu iki değer birbirine eşit değilse bir hata fırlatır.
- assertTrue(condition): Belirtilen koşulun doğru olup olmadığını kontrol eder. Eğer koşul doğru değilse bir hata fırlatır.
- assertFalse(condition): Belirtilen koşulun yanlış olduğunu kontrol eder. Eğer koşul doğru ise bir hata fırlatır.
- assertNotNull(object): Belirtilen nesnenin null olmadığını kontrol eder. Eğer nesne null ise bir hata fırlatır.
- assertNull(object): Belirtilen nesnenin null olduğunu kontrol eder. Eğer nesne null değilse bir hata fırlatır.
- assertThrows(exceptionClass, executable): Belirtilen bir metotun belirli bir türde bir istisna fırlatıp fırlatmadığını kontrol eder. Eğer belirtilen metot istisna fırlatmazsa veya farklı bir türde bir istisna fırlatırsa bir hata fırlatır.
@Test public void whenDivisionByZero_shouldThrowsArithmeticException() { assertThrows(ArithmeticException.class, () -> Calculator.divide(10, 0)); } @Test public void whenSumTwoNumbers_shouldEqualsSumOfTwoNumbers() { int result = Calculator.add(3, 5); assertEquals(8, result); }
- @Mock / @InjectMock: @Mock annotation’ı, Mockito tarafından kullanılan bir annotation’dır ve mock nesnelerini oluşturmak için kullanılır. @InjectMock annotation’ı, bir mock nesnesini bir sınıfa enjekte etmek için kullanılır. Bu nedenle, @Mock ile oluşturulan nesne ve bu nesnenin enjekte edildiği sınıf arasındaki ilişkiyi belirtmek önemlidir.
@Mock private UserService userService; @InjectMocks private UserController userController;
Bu örnekte, userService adında bir mock nesne oluşturduk ve bu nesneyi userController sınıfına enjekte ettik.
Spring Boot ile Unit Test Uygulaması
Yazının buraya kadar olan kısmında JUnit, Mockito ve unit test kavramından bahsettik. Bu bölümde, bir Spring Boot uygulaması için örnek unit testler yazacağız.
İlk olarak uygulamamızın pom.xml dosyasına gerekli dependencyleri ekliyoruz:
<!-- Mockito --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.12.4</version> <scope>test</scope> </dependency> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> <scope>test</scope> </dependency>
package com.onurilyastokay.unittestexample.services; import java.util.ArrayList; import java.util.Collections; public class ListSortService { public ArrayList<Integer> sortList(ArrayList<Integer> list) { Collections.sort(list); return list; } }
Unit Test Oluşturma ve İsimlendirme Standartları
Servisimiz hazır olduğuna göre, tekrar test kısmına geri dönebiliriz. Bu testte, sıralanmamış dummy bir datayı sortList metoduna göndererek metodun döndürdüğü sıralanmış listenin doğruluğunu kontrol edeceğiz. İlk olarak test dosyamızı oluşturalım:
İlk Unit Testimizi Yazalım
Bu bölümde, ListSortService’deki sortList metodu için unit test yazacağız. Bu metodu test etmek için sırasız bir liste örneği oluşturup sortList metoduna göndereceğiz. Sırasız listenin bir sıralı örneğini oluşturarak metottan dönen değer ile sıralı örneği karşılaştırarak dönen değerin doğrulunu test edeceğiz:
package com.onurilyastokay.unittestexample.services; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; import org.junit.jupiter.api.Test; public class ListSortServiceTest { @Test public void whenCallSortListWithIntegerList_shouldReturnSortedList() { ArrayList<Integer> inputList = new ArrayList<>(Arrays.asList(3, 1, 2)); ArrayList<Integer> expectedList = new ArrayList<>(Arrays.asList(1, 2, 3)); ArrayList<Integer> sortedList = new ListSortService().sortList(inputList); assertEquals(expectedList, sortedList); } }
Bu örnekte, inputList adında ve 3,1,2 değerlerini içeren bir ArrayList oluşturduk. Bu ArrayList’i metoda gönderdiğimizde dönmesini beklediğimiz değerleri (1,2,3) expectedList ArrayList’inde tanımladık. Sonrasında, inputList’i sortList metodumuza gönderdik ve metottan dönen değeri beklediğimiz değerlerle karşılaştırdık. Test sonucu:
Mock Annotation Kullanarak Unit Test Yazma
Bu bölümde, @Mock annotation’ı kullanarak basit bir unit test yazacağız. Öncelikle test için gerekli ortamı hazırlayalım:
- User model ve UserService servisi oluşturulmalı.
- User, id ve name özelliklerine sahip olmalı.
- id ve name özellikleri getter/setterlara sahip olmalı.
- Constructor üzerinden User nesnesi yaratılabilmeli.
- UserService içerisinde, User’ın constructor’ını çağıran createUser adında bir metot bulunmalı.
User.java:
package com.onurilyastokay.unittestexample.models; public class User { private Integer id; private String name; public User(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } }
UserService.java:
package com.onurilyastokay.unittestexample.services; import com.onurilyastokay.unittestexample.models.User; public class UserService { public User createUser(Integer id, String name) { return new User(id, name); } }
Bu uygulamanın test kısmında, createUser metoduna id ve name bilgisi göndererek bir User nesnesi oluşturacağız. Sonrasında, oluşturduğumuz nesnenin getterlarını kullanarak gönderdiğimiz id ve name bilgisinin doğruluğunu kontrol edeceğiz. “src/test/java/com/onurilyastokay/unittestexample/services” pathinin altına UserServiceTest.java dosyamızı oluşturarak testimizi yazalım.
UserServiceTest.java:
package com.onurilyastokay.unittestexample.services; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.mockito.Mock; import com.onurilyastokay.unittestexample.models.User; public class UserServiceTest { @Mock private User mockedUser; @Test public void whenCreateUser_shouldEqualsWithIdAndNameParameters() { UserService userService = new UserService(); String name = "Onur"; int id = 1; mockedUser = userService.createUser(id, name); assertEquals(name, mockedUser.getName()); assertEquals(id, mockedUser.getId()); } }
Bu kod örneğinde, @Mock annotation’ı kullanılarak mockedUser isminde dummy bir User nesnesi oluşturduk. Sonrasında id’si 1 olan ve ismi Onur olan bir User oluşturarak mockedUser değişkenine atadık. createUser metoduna parametre olarak geçtiğimiz id ve name bilgisiyle oluşturulan User’ın id ve name bilgisini assertEquals ile karşılaştırdık.
Test Sonucu:
Yaptığımız örneklerde assertEquals kullanarak gönderilen ve alınan verileri karşılaştırdık. Örneğimizi biraz daha genişleterek hatalı kullanım durumlarını da test eden bir uygulama hâline getirelim:
Bu testte parametre olarak gönderdiğimiz id’ye negatif değerli bir sayı atayarak User oluşturabiliriz. Kod içerisinde de bu durumu kontrol eden bir yapı bulunmuyor. UserService.java dosyasında ufak bir değişiklik yaparak negatif id’li kullanıcı oluşturulmasını engelleyebiliriz.
public User createUser(Integer id, String name) { if (id < 0) { throw new IllegalArgumentException(); } return new User(id, name); }
Bu güncellemeden sonra id değerinin negatif olması durumunda uygulamamızın IllegalArgumentException fırlatmasını bekliyoruz. id parametresinin negatif değer olarak gönderildiği bir unit test yazarak kodun doğru çalışıp çalışmadığını kontrol edebiliriz.
@Test public void whenCreateUser_shouldIdCannotBeLessThanZero() { UserService userService = new UserService(); String name = "Onur"; int id = -5; assertThrows(IllegalArgumentException.class, () -> userService.createUser(id, name)); }
Burada idsi -5 olan ve ismi Onur olan bir user oluşturmaya çalışıyoruz. createUser metodu IllegalArgumentException hatası fırlatırsa createUser metoduna eklediğimiz if bloğunun istediğimiz gibi çalışıyor demektir. Bu durumda, test başarılı olarak dönecektir.
Test Sonucu:
Bu yazıda, JUnit, Mockito ve Spring Boot kullanarak basit bir unit test uygulaması geliştirdik. Umarım faydalı olmuştur. Projenin kaynak kodlarına GitHub hesabımdan ulaşabilirsiniz: GitHub | java-unit-test
Başarılı bir anlatım olmuş. Elinize sağlık.
Teşekkürler Burak Bey, faydalı olmasına sevindim.