() İnvocation çalışırken mockito nasıl çalışır?


111

Aşağıdaki Mockito ifadesi göz önüne alındığında:

when(mock.method()).thenReturn(someValue);

Mock.method () ifadesinin dönüş değerini when () 'ye ileteceği düşünüldüğünde, Mockito bir sahte için proxy oluşturmaya nasıl devam ediyor? Bunun bazı CGLib malzemeleri kullandığını hayal ediyorum, ancak bunun teknik olarak nasıl yapıldığını bilmek isterim.

Yanıtlar:


118

Kısa cevap, örneğinizde sonucunun mock.method()türe uygun bir boş değer olacağıdır; mockito MockingProgress, bir taklit üzerinde bir yöntemin çağrılmasının, saplama hakkındaki bilgileri geri dönüş değeri aracılığıyla iletmek yerine, mevcut bir stublanmış davranışın kopyalanması veya yeniden oynatılması için olup olmadığını belirlemek için proxy oluşturma, yöntem durdurma ve sınıfın paylaşılan bir örneğini kullanarak dolaylı yoldan ulaşmayı kullanır . alay konusu bir yöntem.

Mockito koda bakarak birkaç dakika içinde yapılan mini analiz aşağıdaki gibidir. Unutmayın, bu çok kaba bir tanım - burada oyunda pek çok ayrıntı var. Github'daki kaynağı kendiniz kontrol etmenizi öneririm .

Birincisi, sınıfın mockyöntemini kullanarak bir sınıfla dalga geçtiğinizde Mockito, esasen şu olur:

  1. Mockito.mockorg.mockito.internal.MockitoCore.mock'a delegeler , varsayılan sahte ayarları bir parametre olarak iletir.
  2. MockitoCore.mockorg.mockito.internal.util.MockUtil.createMock delegeleri
  3. MockUtilSınıf kullanan ClassPathLoaderbir örneğini almak için sınıf MockMakeralay oluşturmak için kullanmak. Varsayılan olarak, CgLibMockMaker sınıfı kullanılır.
  4. CgLibMockMakerJMock'tan ödünç alınan, taklidi ClassImposterizeroluşturmayı sağlayan bir sınıf kullanır . Kullanılan 'Mockito büyü' kilit nitelikteki MethodInterceptorMockito: mock oluşturmak için kullanılan MethodInterceptorFilterve bir örneği de dahil olmak üzere MockHandler örnekleri, bir zincir MockHandlerImpl . Yöntemi engelleyici, çağrıları MockHandlerImpl örneğine iletir; bu, bir yöntem bir sahte üzerinde çağrıldığında uygulanması gereken iş mantığını uygular (yani, bir yanıtın önceden kaydedilip kaydedilmediğini görmek için arama yapmak, çağrının yeni bir saplamayı temsil edip etmediğini belirlemek vb.) Varsayılan durum, çağrılan yöntem için bir saplama önceden kayıtlı değilse, türe uygun boş bir değerin döndürülmesidir.

Şimdi, örneğinizdeki koda bakalım:

when(mock.method()).thenReturn(someValue)

İşte bu kodun çalıştırılacağı sıra:

  1. mock.method()
  2. when(<result of step 1>)
  3. <result of step 2>.thenReturn

Neler olup bittiğini anlamanın anahtarı, sahte yöntem çağrıldığında ne olduğudur: yöntem durdurucu, yöntem çağrısı hakkında bilgi aktarılır ve MockHandlersonunda temsilci olarak atanan örnekler zincirine delege eder MockHandlerImpl#handle. Sırasında MockHandlerImpl#handle, sahte işleyici bir örnek oluşturur OngoingStubbingImplve bunu paylaşılan MockingProgressörneğe aktarır.

Tüm whenyöntem çağırma sonra çağrılır method()için temsilcilerin bulunduğu, MockitoCore.whenaramaları, stub()aynı sınıfın bir yöntem. Bu yöntem MockingProgress, alay edilen method()çağrının yazdığı paylaşılan örnekten devam eden saplamayı çözer ve onu döndürür. Daha sonra thenReturnyöntem, OngoingStubbingörnekte çağrılır .


1
Detaylı cevap için teşekkürler. Başka bir soru - "method () 'un çağrılmasından sonra metod çağrıldığında" bahsediyorsunuz - () metodunun bir sonraki çağrı olduğunu (veya metod () çağrısını sarmaladığını) nasıl anlar? Umarım mantıklıdır.
marchaos

@marchaos Bilmiyor. İle when(mock.method()).thenXyz(...)sözdizimi, mock.method()değil "stubbing" modunda, "yeniden" modunda çalıştırılmaktadır. Normalde, bu yürütme mock.method()kadar sonra zaman hiçbir etkisi yoktur thenXyz(...)( thenReturn, thenThrow, thenAnswervs) işletilirse, bu modu "stubbing" girer ve sonra bu yöntem çağrısı için istenilen sonucu kaydeder.
Rogério

1
Rogerio, aslında bundan biraz daha incelikli - mockito'nun açık saplama ve tekrar oynatma modları yok. Cevabımı daha sonra düzenleyeceğim, böylece daha net olacak.
Paul Morie

Kısacası, CGLIB veya Javassist ile başka bir yöntemdeki bir yöntem çağrısını durdurmak daha kolaydır, örneğin "eğer" operatörü ile araya girmek daha kolaydır.
Infeligo

Açıklamama henüz burada masaj yapmadım, ama ben de unutmadım. Bilginize.
Paul Morie

33

Kısa cevap, perde arkasında Mockito'nun yöntem saplama oluşturma adımlarının bilgilerini kaydetmek için bir tür global değişkenler / depolama kullanır (örneğinizde method (), when (), thenReturn ()), böylece sonunda Hangi paramda çağrıldığında neyin döndürülmesi gerektiğine dair bir harita oluşturun.

Bu makaleyi çok yararlı buldum: Proxy tabanlı Mock Frameworks'ün nasıl çalıştığını açıklama ( http://blog.rseiler.at/2014/06/explanation-how-proxy-based-mock.html ). Yazar, bu Mocking çerçevelerinin nasıl çalıştığını anlamak isteyenler için çok iyi bir kaynak bulduğum bir gösteri Mocking çerçevesi uyguladı.

Bana göre bu tipik bir Anti-Pattern kullanımıdır. Normalde bir yöntemi uygularken 'yan etkiden' kaçınmalıyız, yani yöntemin girdiyi kabul etmesi ve bazı hesaplamalar yapıp sonucu döndürmesi gerekir - bunun dışında hiçbir şey değişmedi. Ancak Mockito bu kuralı kasıtlı olarak ihlal ediyor. Yöntemleri, sonucu döndürmenin yanı sıra bir dizi bilgiyi saklar: Mockito.anyString (), mockInstance.method (), when (), thenReturn, hepsinin özel 'yan etkisi' vardır. Bu yüzden çerçeve ilk bakışta bir sihir gibi görünüyor - genellikle böyle kod yazmayız. Bununla birlikte, alaycı çerçeve durumunda, bu anti-pattern tasarımı, çok basit bir API'ye yol açtığı için harika bir tasarımdır.


4
Mükemmel bağlantı. Bunun arkasındaki deha şudur: Her şeyin çok güzel görünmesini sağlayan çok basit API. Bir diğer önemli karar, when () yönteminin jenerikler kullanmasıdır, böylece thenReturn () yöntemi tür için güvenli olur.
David Tonhofer

Bunun daha iyi bir cevap olduğunu düşünüyorum. Diğer cevabın aksine, somut kod üzerinden kontrol akışı yerine alay süreci kavramlarını açıkça açıklıyor. Katılıyorum, mükemmel bağlantı.
mihca
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.