Mockito, Java ve Kotlin birim testlerinde mock nesneleri oluşturmak için açık kaynaklı bir framework'tür ve test edilen kodu harici bağımlılıklardan yalıtmayı sağlar. Yardımıyla, geliştirici gerçek depoları, API istemcilerini ve veritabanlarını önceden tanımlanmış davranışa sahip kontrollü stub'larla değiştirir. Mockito.org'ya göre, kütüphane birim test kullanan Java projelerinin %60'ından fazlasında kullanılmaktadır.
Kilit noktalar
Mockito, Java, Kotlin ve diğer JVM dillerindeki birim testler için mock nesneleri (stub) oluşturmaya yarayan açık kaynaklı bir kütüphanedir. Test yürütmesinden sorumlu olan JUnit'in aksine, Mockito yalıtım sorununu çözer — test edilen sınıfın gerçek bağımlılıklarını öngörülebilir nesnelerle değiştirir.
Mock olmadan, bir veritabanına veya harici API'ye erişen bir metodu test etmek, gerçek bir ortam kurulumu gerektirir — veritabanı dağıtımı, sunucu başlatma. Mockito bu bağımlılıkları sabit davranışa sahip nesnelerle değiştirir: repository.findById(1) metodu, veritabanına erişmeden her zaman belirli bir User nesnesini döndürür.
Mockito'nun mimarisi Proxy desenine (arabirimler ve sınıflar için) dayanır. Kütüphane, belirtilen tür için bir alt sınıf veya proxy oluşturur ve tüm metot çağrılarını keserek varsayılan değerleri veya when().thenReturn() ile ayarlanmış değerleri döndürür.
Mockito'nun çalışma prensibi üç temel işleme dayanır: mock oluşturma, davranış yapılandırma (stubbing) ve çağrıları doğrulama (verification). Her işlem, Maven Central istatistiklerine göre Java ekosisteminde en çok indirilen sınıf olan org.mockito.Mockito sınıfındaki statik metotları kullanır. Tüm mock metot çağrıları bellekte kaydedilir ve daha sonra verify ile doğrulamaya olanak tanır.
Mockito ile tipik bir test üç aşamadan oluşur: Arrange — when().thenReturn() ile mock oluşturma ve stub yapılandırma, Act — test edilen metodu çağırma, Assert — assertEquals ve verify(mock) ile sonucu kontrol etme. Bu yaklaşıma AAA (Arrange-Act-Assert) denir.
Mockito'nun bir kullanıcı deposunu değiştirdiği basit bir teste bakalım. when().thenReturn() metodu, findById çağrısının önceden hazırlanmış bir User nesnesi döndürmesi için mock'u yapılandırır.
// Bir depo mock'u oluşturma
UserRepository mockRepo = mock(UserRepository.class);
// Davranışı yapılandırma: findById(1) bir kullanıcı döndürür
when(mockRepo.findById(1)).thenReturn(new User("Alice"));
// Metodun gerçekten çağrıldığını kontrol etme
User result = mockRepo.findById(1);
assertEquals("Alice", result.getName());
verify(mockRepo).findById(1);
Mockito, mock oluşturmak için iki yol sunar: statik metot mock(Class) ve MockitoAnnotations.openMocks() ile başlatma ile @Mock anotasyonu. İlk yaklaşım bir veya iki mock için derli topludur, ikincisi çok sayıda bağımlılık olduğunda kullanışlıdır — anotasyonlar boilerplate kodunu azaltır.
mock() metodu bir sınıf alır ve when().thenReturn() ile yapılandırılabilen bir stub nesnesi döndürür. Yapılandırılmamış tüm metotlar varsayılan değerleri döndürür: sayılar için 0, boolean için false, nesneler için null.
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);
@Mock anotasyonu, @ExtendWith(MockitoExtension.class) ile birleştiğinde test sınıfının tüm alanları için otomatik olarak mock oluşturur. MockitoExtension, her testten önce başlatmadan sorumludur.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void getUserShouldReturnUserFromRepo() {
when(userRepository.findById(1)).thenReturn(new User("Alice"));
User result = userService.getUser(1);
assertEquals("Alice", result.getName());
}
}
Stubbing, bir mock metodunun belirli argümanlarla çağrıldığında ne döndürmesi gerektiğini tanımlama sürecidir. Temel sözdizimi: when(mock.method(args)).thenReturn(value). Farklı senaryolar için Mockito, çeşitli then-metot varyantları sunar.
| Metot | Amaç |
|---|---|
| thenReturn(value) | Her zaman belirtilen değeri döndürür |
| thenThrow(exception) | Çağrıldığında istisna fırlatır |
| thenAnswer(answer) | Dönüş değerini dinamik olarak hesaplar |
| thenCallRealMethod() | Gerçek metodu çağırır (kısmi mock) |
Dönüş değeri çağrı argümanlarına bağlı olduğunda, lambda ile thenAnswer kullanılır. Bu, gerçek verilerle çalışmayı simüle etmek için kullanışlıdır — örneğin, iletilen nesneye göre bir ID oluşturma.
when(repository.save(any())).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(42);
return user;
});
Verify, daha eski mock nesne kütüphanelerinin (EasyMock, jMock) sağlamadığı, Mockito'nun benzersiz bir özelliğidir.
Verify, testleri daha güvenilir hale getirir çünkü yalnızca dönüş değerini değil, aynı zamanda yan etkileri de — sonuç döndürmeyen metot çağrılarını (void metotlar) — kontrol eder. verify(mock).methodName(args) metodu, belirli bir mock metodunun belirtilen argümanlarla çağrılıp çağrılmadığını kontrol eder. Bu, yalnızca sonucu değil, aynı zamanda süreci — bağımlılığa erişme olayını — test etmeye olanak tanır.
Varsayılan olarak verify, metodun tam olarak bir kez çağrıldığını kontrol eder. Farklı bir sayı gerekiyorsa, Mockito sınıfından times(n), atLeast(n), never() ve diğer değiştiriciler kullanılır.
// Çağrı sayısını kontrol etme
verify(repository, times(3)).save(any());
verify(repository, never()).delete(any());
verify(repository, atLeastOnce()).findById(1);
// Çağrı sırasını kontrol etme
InOrder inOrder = inOrder(repository);
inOrder.verify(repository).save(any());
inOrder.verify(repository).flush();
Bir metodun hangi nesneyle çağrıldığını tam olarak kontrol etmek gerektiğinde, ArgumentCaptor kullanılır. Çağrı sırasında argüman değerini yakalar ve alanlarını ayrı ayrı kontrol etmeye olanak tanır. ArgumentCaptor, özellikle test edilen kodun dahili olarak bir nesne oluşturup bunu bir bağımlılığa aktarması durumunda kullanışlıdır — aksi takdirde bu nesneyi kontrol edemezsiniz.
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());
@Mock ve @InjectMocks, boilerplate kodunu önemli ölçüde azaltan iki temel Mockito anotasyonudur. @Mock bir alan için mock oluşturur ve @InjectMocks, test sınıfındaki tüm mock'ları yapıcı, ayarlayıcı veya alan aracılığıyla test edilen nesneye enjekte eder.
@InjectMocks mekanizması, bağımlılıkları şu sırayla enjekte etmeye çalışır: en çok argümana sahip yapıcı, türe göre ayarlayıcı, özel alan. Bu yöntemlerden hiçbiri çalışmazsa, nesne null bağımlılıklarla kalır ve test NullPointerException ile başarısız olur.
Şunu anlamak önemlidir: @InjectMocks alan türlerini analiz etmez — türle uyumlu herhangi bir mock'u değiştirir. Bir sınıfta aynı türde iki alan varsa, Mockito yanlış mock'u enjekte edebilir. Bu gibi durumlarda, mock parametreleriyle açık bir yapıcı kullanılması önerilir.
Android geliştirmede, Mockito ViewModel, Repository ve UseCase'i test etmek için JUnit ile birlikte kullanılır. Bu sınıflar Android bağlamı olmadan JVM'de çalıştığından, Mockito bağımlılıklarını — Room DAO, Retrofit API, SharedPreferences — öngörülebilir davranışa sahip stub'larla değiştirir.
Bir Android projesine Mockito eklemek için, mockito-core veya mockito-inline bağımlılığını eklemeniz yeterlidir (ikincisi, final sınıfların ve statik metotların mocklanmasını destekler). Sürüm 5.12.0 (2024), Java 21 desteği ve geliştirilmiş JUnit 5 entegrasyonu içerir.
// build.gradle.kts (module)
dependencies {
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}
Daha önce, statik metotları ve yapıcıları mocklamak için PowerMock — bayt kodu enstrümantasyonu yoluyla çalışan bir uzantı — gerekiyordu. Mockito 5.x ve sonrasında mockito-inline ile bu yetenek doğrudan entegre edilmiştir: mockStatic(ClassName.class), ek kütüphanelere gerek kalmadan statik metotların mocklanmasına olanak tanır.
Tipik bir senaryo: bir ViewModel, bir depo metodunu çağırır ve sonucu UI durumuna dönüştürür. Mockito depoyu değiştirir ve test, ViewModel'in hem başarılı yanıtı hem de hatayı doğru şekilde işlediğini kontrol eder. Clean Architecture kullanırken, her katman için mock'lar oluşturulur: DataSource, Repository ve UseCase — bu, her katmanı ayrı ayrı test etmeye olanak tanır.
Sıkça sorulan sorular
Mockito, proxy ve yansıma kullanan Java ve Kotlin için bir kütüphanedir. MockK, ek yapılandırma olmadan coroutine, genişletme işlevleri ve final sınıfları destekleyen Kotlin-öncelikli bir kütüphanedir.
Mockito 2.1'den itibaren, final sınıfların mocklanması opt-in yoluyla desteklenmektedir. 5.x sürümünde (mockito-inline), bu varsayılan olarak etkindir. Sadece mockito-inline bağımlılığını ekleyin ve standart mock() yöntemini kullanın.
Spy, varsayılan olarak gerçek metotları çağıran ancak when().thenReturn() ile bazılarını geçersiz kılmaya izin veren kısmi bir mock'tur. Spy, tüm sınıfın yeniden yazılamadığı eski kodları test etmek için kullanışlıdır.
thenReturn, argümanlardan bağımsız olarak her zaman aynı değeri döndürür. thenAnswer, çağrı — çağrı argümanları, mock'un kendisi ve durum — temelinde dönüş değerini hesaplar. Dinamik yanıtlar için her zaman thenAnswer kullanın.
Verify yalnızca sonucu değil, aynı zamanda süreci — bağımlılığa erişme olayını — de kontrol eder. Bu, veri kaydetmesi veya bildirim göndermesi gereken hizmetler için kritiktir. Verify olmadan, test bir metodun save() veya send() işlevini çağırmadığını tespit edemez.
Ö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