Mockito kullanırken alay ve casusluk arasındaki fark nedir?


137

Mockito casusu kullanımı için bir kullanım durumu ne olurdu?

Bana öyle geliyor ki her casus kullanım durumu callRealMethod kullanılarak bir alayla ele alınabiliyor.

Görebildiğim bir fark, çoğu yöntem çağrısının gerçek olmasını istiyorsanız, bir casusa karşı bir sahte kullanmak için bazı kod satırlarını kaydeder. Bu mu yoksa daha büyük resmi mi kaçırıyorum?

Yanıtlar:


100

Cevap belgelerde :

Gerçek kısmi alaylar (1.8.0'dan beri)

Son olarak, posta listesindeki birçok dahili tartışma ve tartışmadan sonra, Mockito'ya kısmi sahte destek eklendi. Daha önce kısmi alayları kod kokusu olarak kabul etmiştik. Ancak, kısmi alaylar için meşru bir kullanım durumu bulduk.

Yayınlamadan önce 1.8 casus () gerçek kısmi alaylar üretmiyordu ve bazı kullanıcılar için kafa karıştırıcıydı. Casusluk hakkında daha fazla bilgi için: burada veya javadoc for spy (Object) yöntemi.

callRealMethod()sonra tanıtıldı spy(), ancak geriye dönük uyumluluk sağlamak için casus () orada kaldı.

Aksi takdirde haklısınız: bir casusun yöntemleri tüm stubbe olmadıkça gerçektir. Sahte bir yöntem callRealMethod()çağrılmadığı sürece inatçıdır . Genel olarak, kullanmayı tercih ederim callRealMethod(), çünkü beni doXxx().when()geleneksel yerine deyimi kullanmaya zorlamıyorwhen().thenXxx()


Bu durumlarda alaycı casus tercih etme problemi, sınıfın kendisine enjekte edilmemiş (ancak yerel olarak başlatılan) bir üyeyi kullanması ve daha sonra “gerçek” yöntem tarafından kullanılmasıdır; sahte, üye varsayılan Java değeri olarak başlatılır, bu da yanlış davranış ve hatta bir NullPointerException neden olabilir. Bunu geçmenin yolu bir "init" yöntemi eklemek ve daha sonra "gerçekten" çağırmaktır, ama bu bana biraz abartılı geliyor.
Eyal Roth

Dokümandan: "Casuslar, örneğin eski kodlarla uğraşırken dikkatli ve zaman zaman kullanılmalıdır." Birim test alanı aynı şeyi yapmanın çok fazla yolundan muzdariptir.
gdbj

89

Casus ve Sahte Arasındaki Fark

Mockito bir alay yarattığında - bunu gerçek bir örnekten değil bir Tür Sınıfından yapar. Alaycı, sınıfın çıplak kemikli bir kabuk örneğini yaratır, tamamen onunla etkileşimleri izlemek için kullanılır. Öte yandan, casus mevcut bir örneği sarar. Yine de normal örnekle aynı şekilde davranacaktır - tek fark, onunla tüm etkileşimleri izlemek için de araç haline getirilmesidir.

Aşağıdaki örnekte - ArrayList sınıfının bir örneğini oluşturuyoruz:

@Test
public void whenCreateMock_thenCreated() {
    List mockedList = Mockito.mock(ArrayList.class);

    mockedList.add("one");
    Mockito.verify(mockedList).add("one");

    assertEquals(0, mockedList.size());
}

Gördüğünüz gibi, alay konusu listeye bir öğe eklemek aslında hiçbir şey eklemiyor - sadece yöntemi başka bir yan etkisi olmadan çağırıyor. Öte yandan bir casus farklı davranır - aslında add yönteminin gerçek uygulamasını çağırır ve öğeyi temel listeye ekler:

@Test
public void whenCreateSpy_thenCreate() {
    List spyList = Mockito.spy(new ArrayList());
    spyList.add("one");
    Mockito.verify(spyList).add("one");

    assertEquals(1, spyList.size());
}

Burada, nesnenin gerçek dahili yönteminin çağrıldığını söyleyebiliriz, çünkü size () yöntemini çağırdığınızda boyutu 1 olarak alırsınız, ancak bu size () yöntemi alay edilmez! Peki 1 nereden geliyor? İç gerçek size () yöntemi, boyut () alay edilmediği (veya saplanmadığı) olarak adlandırılır ve bu nedenle girişin gerçek nesneye eklendiğini söyleyebiliriz.

Kaynak: http://www.baeldung.com/mockito-spy + öz notlar.


1
Size () 1 döndürür mü demek istiyorsun?
siyah

İlk örnekte, bu yöntem de kaldırılmadıysa neden mockedList.size()geri dönüyor 0? Bu, yöntemin dönüş türü verildiğinde varsayılan bir değer mi?
mike

@mike: Java'da mockedList.size()bir intve varsayılan değeri int0 olur. assertEquals(0, mockedList.size());Sonra çalıştırmayı denerseniz mockedList.clear();, sonuç aynı kalır.
realPK

2
Bu cevap iyi ve basit bir şekilde yazılmış ve nihayet sahte ve casus arasındaki farkı anlamama yardımcı oldu. Güzel bir.
PesaThe

38

8 yöntemli bir nesne varsa ve 7 gerçek yöntemi ve saplama yöntemini çağırmak istediğiniz bir testiniz varsa iki seçeneğiniz vardır:

  1. Bir sahte kullanarak 7 callRealMethod ve stub one yöntemini çağırarak onu kurmanız gerekir.
  2. Bir kullanarak, spybir yöntemi saplayarak ayarlamanız gerekir

Resmi belgeler üzerinde doCallRealMethodkısmi mocks için casus kullanılmasını önerir.

Kısmi alaylar hakkında daha fazla bilgi için ayrıca bkz. Javadoc casusu (Nesne). Mockito.spy () kısmi alaylar oluşturmanın önerilen bir yoludur. Bunun nedeni, spy () yöntemine iletilen nesneyi oluşturmaktan sorumlu olduğunuz için gerçek yöntemlerin doğru yapılandırılmış nesneye karşı çağrılmasını garanti etmesidir.


5

Eski kod için birim testleri oluşturmak istediğinizde casus yararlı olabilir .

Burada çalıştırılabilir bir örnek oluşturdum https://www.surasint.com/mockito-with-spy/ , bazılarını buraya kopyalarım.

Bu kod gibi bir şey varsa:

public void transfer(  DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService, 
             double amount, String fromAccount, String toAccount){
    withdrawMoneyService.withdraw(fromAccount,amount);
    depositMoneyService.deposit(toAccount,amount);
}

Casusa ihtiyacınız olmayabilir çünkü DepositMoneyService ve WithdrawMoneyService ile alay edebilirsiniz.

Ancak, bazı eski kodlarda, bağımlılık kodu şu şekildedir:

    public void transfer(String fromAccount, String toAccount, double amount){

        this.depositeMoneyService = new DepositMoneyService();
        this.withdrawMoneyService = new WithdrawMoneyService();

        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }

Evet, ilk koda geçebilirsiniz ancak API değiştirilir. Bu yöntem birçok yer tarafından kullanılıyorsa, hepsini değiştirmeniz gerekir.

Alternatif olarak, bağımlılığı şu şekilde çıkarabilirsiniz:

    public void transfer(String fromAccount, String toAccount, double amount){
        this.depositeMoneyService = proxyDepositMoneyServiceCreator();
        this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator();

        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }
    DepositMoneyService proxyDepositMoneyServiceCreator() {
        return new DepositMoneyService();
    }

    WithdrawMoneyService proxyWithdrawMoneyServiceCreator() {
        return new WithdrawMoneyService();
    }

Sonra casus enjekte edebilirsiniz bağımlılığı şöyle:

DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class);
        WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class);

    TransferMoneyService target = spy(new TransferMoneyService());

    doReturn(mockDepositMoneyService)
            .when(target).proxyDepositMoneyServiceCreator();

    doReturn(mockWithdrawMoneyService)
            .when(target).proxyWithdrawMoneyServiceCreator();

Yukarıdaki bağlantıda daha fazla ayrıntı.


0

Mockçıplak bir çift nesnedir. Bu nesne aynı yöntem imzalarına sahip ancak gerçekleştirme boş ve varsayılan değer olan 0 ve null değerini döndürüyor

Spyklonlanmış bir çift nesnedir. Yeni nesne gerçek bir nesneye dayalı olarak klonlanır, ancak onu alay etme olasılığınız vardır

class A {

    String foo1() {
        foo2();
        return "RealString_1";
    }

    String foo2() {
        return "RealString_2";
    }

    void foo3() {
        foo4();
    }

    void foo4() {

    }
}
@Test
public void testMockA() {

    //given
    A mockA = Mockito.mock(A.class);
    Mockito.when(mockA.foo1()).thenReturn("MockedString");

    //when
    String result1 = mockA.foo1();
    String result2 = mockA.foo2();

    //then
    assertEquals("MockedString", result1);
    assertEquals(null, result2);

    //Case 2
    //when
    mockA.foo3();

    //then
    verify(mockA).foo3();
    verify(mockA, never()).foo4();
}

@Test
public void testSpyA() {
    //given
    A spyA = Mockito.spy(new A());

    Mockito.when(spyA.foo1()).thenReturn("MockedString");

    //when
    String result1 = spyA.foo1();
    String result2 = spyA.foo2();

    //then
    assertEquals("MockedString", result1);
    assertEquals("RealString_2", result2);

    //Case 2
    //when
    spyA.foo3();

    //then
    verify(spyA).foo3();
    verify(spyA).foo4();
}

[Çift türleri test et]

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.