Yapıcıyı parametreyle taklit edin


90

Aşağıdaki gibi bir sınıfım var:

public class A {
    public A(String test) {
        bla bla bla
    }

    public String check() {
        bla bla bla
    }
}

Yapıcıdaki mantık A(String test)ve check()alay etmeye çalıştığım şeyler. Şunun gibi aramalar istiyorum: new A($$$any string$$$).check()kukla bir dize döndürür "test".

Denedim:

 A a = mock(A.class); 
 when(a.check()).thenReturn("test");

 String test = a.check(); // to this point, everything works. test shows as "tests"

 whenNew(A.class).withArguments(Matchers.anyString()).thenReturn(rk);
 // also tried:
 //whenNew(A.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(rk);

 new A("random string").check();  // this doesn't work

Ama işe yaramıyor gibi görünüyor. new A($$$any string$$$).check()hala alay konusu nesneyi getirmek yerine yapıcı mantığından geçiyor A.


Sahte check () yönteminiz doğru çalışıyor mu?
Ben Glasser

@BenGlasser check () çalışıyor. Sadece New hiç çalışmadığı zaman. Açıklamayı da güncelledim.
Shengjie

Yanıtlar:


93

Gönderdiğiniz kod, Mockito ve Powermockito'nun en son sürümüyle benim için çalışıyor. Belki A'yı hazırlamadın? Bunu dene:

A. java

public class A {
     private final String test;

    public A(String test) {
        this.test = test;
    }

    public String check() {
        return "checked " + this.test;
    }
}

MockA.java

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class MockA {
    @Test
    public void test_not_mocked() throws Throwable {
        assertThat(new A("random string").check(), equalTo("checked random string"));
    }
    @Test
    public void test_mocked() throws Throwable {
         A a = mock(A.class); 
         when(a.check()).thenReturn("test");
         PowerMockito.whenNew(A.class).withArguments(Mockito.anyString()).thenReturn(a);
         assertThat(new A("random string").check(), equalTo("test"));
    }
}

Her iki test de mockito 1.9.0, powermockito 1.4.12 ve junit 4.8.2 ile geçmelidir.


25
Ayrıca kurucu başka bir sınıftan çağrıldıysa, onu listeye dahil edinPrepareForTest
Jeff E

"PowerMockito.whenNew" çağrıldığında kendini neden hazırlamamız gerektiğine dair bir fikri olan var mı?
udayanga

50

Benim bildiğim kadarıyla, kurucularla alay edemezsiniz, sadece yöntemlerle. Ancak Mockito google kod sayfasındaki wiki'ye göre, sınıfınızda o sınıfın yeni bir örneğini döndüren bir yöntem oluşturarak yapıcı davranışıyla dalga geçmenin bir yolu vardır. o zaman bu yöntemle alay edebilirsiniz. Aşağıda doğrudan Mockito wiki'den bir alıntı var :

Kalıp 1 - nesne oluşturmak için tek satırlık yöntemler kullanmak

1. kalıbı kullanmak için (Sınıfım adlı bir sınıfı test etmek), aşağıdaki gibi bir çağrıyı değiştirirsiniz:

   Foo foo = new Foo( a, b, c );

ile

   Foo foo = makeFoo( a, b, c );

ve tek satırlık bir yöntem yazın

   Foo makeFoo( A a, B b, C c ) { 
        return new Foo( a, b, c );
   }

Yönteme herhangi bir mantık eklememeniz önemlidir; sadece nesneyi oluşturan tek çizgi. Bunun nedeni, yöntemin kendisinin asla birim testine tabi tutulmayacağıdır.

Sınıfı test etmeye geldiğinizde, test ettiğiniz nesne aslında bir Mockito casusu olacak ve bu yöntem geçersiz kılınarak bir sahte geri dönecek. Bu nedenle test ettiğiniz şey sınıfın kendisi değil, çok az değiştirilmiş bir versiyonudur.

Test sınıfınız aşağıdaki gibi üyeler içerebilir:

  @Mock private Foo mockFoo;
  private MyClass toTest = spy(new MyClass());

Son olarak, test yönteminizin içinde, makeFoo çağrısını aşağıdaki gibi bir satırla alay edersiniz:

  doReturn( mockFoo )
      .when( toTest )
      .makeFoo( any( A.class ), any( B.class ), any( C.class ));

Yapıcıya iletilen bağımsız değişkenleri kontrol etmek istiyorsanız, herhangi bir () den daha spesifik eşleştiricileri kullanabilirsiniz.

Sadece sınıfınızın alay konusu olan bir nesnesini iade etmek istiyorsanız, bunun sizin için çalışacağını düşünüyorum. Her durumda, alay konusu nesne oluşturma hakkında daha fazla bilgiyi buradan okuyabilirsiniz:

http://code.google.com/p/mockito/wiki/MockingObjectCreation


21
+1, kaynak kodumu ayarlamam gerekmesi onu daha gerçekçi hale getirmesini sevmiyorum. Paylaşım için teşekkürler.
Shengjie

23
Kodunuzu yazarken daha test edilebilir bir kaynak koduna sahip olmak veya test edilebilirlik karşıtı modellerden kaçınmak asla kötü değildir. Daha test edilebilir bir kaynak yazarsanız, otomatik olarak daha sürdürülebilir hale gelir. Yapıcı çağrılarınızı kendi yöntemlerinde izole etmek, bunu başarmanın yollarından yalnızca biridir.
Dawood ibn Kareem

1
Test edilebilir kod yazmak iyidir. A sınıfını yeniden tasarlamaya zorlanmak, böylece A'ya bağlı olan B sınıfı için testler yazabilirim, çünkü A'nın C'ye sabit kodlanmış bir bağımlılığı var, daha az iyi hissettiriyor. Evet, sonunda kod daha iyi olacak, ancak bir test yazmayı bitirmek için kaç tane sınıf yeniden tasarlayacağım?
Mark Wood

@MarkWood benim deneyimlerime göre hantal test deneyimleri genellikle bazı tasarım kusurlarının bir işaretidir. IRL, kurucuları test ediyorsanız, kodunuz muhtemelen bir fabrika veya bağımlılık enjeksiyonu için size bağırıyor demektir. Bu iki durum için tipik tasarım modellerini izlerseniz, kodunuzun test edilmesi ve genel olarak üzerinde çalışılması çok daha kolay hale gelir. Oluşturucuları test ediyorsanız, çünkü orada bir sürü mantık var, muhtemelen bir çok biçimlilik katmanına ihtiyacınız var veya bu mantığı bir başlatma yöntemine taşıyabilirsiniz.
Ben Glasser

12

Powermock Kullanmadan .... Bunu anlamam biraz zaman aldığından, Ben Glasser yanıtına dayanan aşağıdaki örneğe bakın .. umarım bu biraz zaman kazandırır ...

Orijinal Sınıf:

public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(new BClazz(cClazzObj, 10));
    } 
}

Değiştirilmiş Sınıf:

@Slf4j
public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(getBObject(cClazzObj, 10));
    }

    protected BClazz getBObject(CClazz cClazzObj, int i) {
        return new BClazz(cClazzObj, 10);
    }
 }

Test Sınıfı

public class AClazzTest {

    @InjectMocks
    @Spy
    private AClazz aClazzObj;

    @Mock
    private CClazz cClazzObj;

    @Mock
    private BClazz bClassObj;

    @Before
    public void setUp() throws Exception {
        Mockito.doReturn(bClassObj)
               .when(aClazzObj)
               .getBObject(Mockito.eq(cClazzObj), Mockito.anyInt());
    }

    @Test
    public void testConfigStrategy() {
        aClazzObj.updateObject(cClazzObj);

        Mockito.verify(cClazzObj, Mockito.times(1)).setBundler(bClassObj);
    }
}

7

Mockito ile, withSettings () 'i kullanabilirsiniz, örneğin CounterService 2 bağımlılık gerektiriyorsa, bunları sahte olarak iletebilirsiniz:

UserService userService = Mockito.mock(UserService.class); SearchService searchService = Mockito.mock(SearchService.class); CounterService counterService = Mockito.mock(CounterService.class, withSettings().useConstructor(userService, searchService));


Bence en kolay ve en iyi cevap. Teşekkür ederim.

4

Mockito, nihai, statik ve özel yöntemleri test eden sınırlamalara sahiptir.

jMockit test kitaplığı ile, aşağıdaki gibi birkaç şeyi çok kolay ve doğrudan yapabilirsiniz:

Java.io.File sınıfının sahte yapıcısı:

new MockUp<File>(){
    @Mock
    public void $init(String pathname){
        System.out.println(pathname);
        // or do whatever you want
    }
};
  • genel kurucu adı $ init ile değiştirilmelidir
  • atılan argümanlar ve istisnalar aynı kalır
  • dönüş türü geçersiz olarak tanımlanmalıdır

Statik bir yöntemle alay edin:

  • yöntem sahte imzasından durağanlığı kaldırın
  • aksi takdirde yöntem imzası aynı kalır
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.