2012-11-13 27 views
55

ho una classe come di seguito:Mock un costruttore con parametri

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

    public String check() { 
     bla bla bla 
    } 
} 

La logica nel costruttore A(String test) e check() sono le cose che sto cercando di prendere in giro. Voglio qualsiasi chiamata come: new A($$$any string$$$).check() restituisce una stringa fittizia "test".

ho provato:

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 

Ma non sembra funzionare. new A($$$any string$$$).check() sta ancora utilizzando la logica del costruttore anziché recuperare l'oggetto deriso di A.

+0

è il vostro assegno deriso() metodo funziona correttamente? –

+0

@BenGlasser check() funziona bene. Solo il momento in cui New sembra non funzionare affatto. Ho aggiornato anche la descrizione. – Shengjie

risposta

56

Il codice che hai postato lavori per me con l'ultima versione di Mockito e Powermockito. Forse non hai preparato A? Prova questo:

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")); 
    } 
} 

Entrambi i test dovrebbero passare con Mockito 1.9.0, 1.4.12 e powermockito JUnit 4.8.2

+13

Si noti inoltre che se il costruttore viene chiamato da un'altra classe, includerlo nell'elenco in "PrepareForTest". –

33

Per quanto ne so, non è possibile prendere in giro i costruttori con mockito, solo metodi. Ma secondo il wiki sulla pagina di codice di google Mockito c'è un modo per prendere in giro il comportamento del costruttore creando un metodo nella classe che restituisce una nuova istanza di quella classe. allora puoi prendere in giro quel metodo. Qui di seguito è un excerpt directly from the Mockito wiki:

modello 1 - utilizzando metodi di una linea per la creazione di oggetti

da usare modello 1 (test di una classe chiamata MyClass), si dovrebbe sostituire una chiamata come

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

con

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

e scrivere un meto sola riga d

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

È importante non includere alcuna logica nel metodo; solo la riga che crea l'oggetto . La ragione di ciò è che il metodo stesso non va mai a per essere testato unitamente.

Quando si verifica la classe, l'oggetto da testare sarà in realtà una spia Mockito , con questo metodo sovrascritto, per restituire un mock . Quello che stai testando non è quindi la classe stessa, ma una versione leggermente modificata di .

tua classe di test potrebbe contenere membri piace

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

Infine, all'interno del vostro metodo di prova si deridere la chiamata a makeFoo con una linea come

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

È possibile utilizzare matchers che sono più specifico di qualsiasi() se si desidera controllare gli argomenti che vengono passati al costruttore.

Se vuoi solo restituire un oggetto deriso della tua classe, penso che questo dovrebbe funzionare per te.In ogni caso si può leggere di più su beffarda la creazione di oggetti qui:

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

+10

+1, non mi piace il fatto che ho bisogno di modificare il mio codice sorgente rendendolo più mockito amichevole. Grazie per la condivisione. – Shengjie

+15

Non è mai male avere il codice sorgente più testabile, o evitare anti-pattern di testabilità quando scrivi il tuo codice. Se scrivi fonte che è più testabile, è automaticamente più manutenibile. Isolare le chiamate del costruttore con i propri metodi è solo un modo per ottenere ciò. –

8

Senza utilizzare Powermock .... Vedere l'esempio qui sotto basato sulla risposta di Ben Glasser poiché mi ci è voluto un po 'di tempo per capirlo ... spero che si salvino alcune volte ...

Classe originale:

public class AClazz { 

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

Classe Modificato:

@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); 
    } 
} 

Classe test

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); 
    } 
} 
1

Mockito ha dei limiti collaudo finale, STA tic e metodi privati.

con biblioteca test JMockit, si possono fare cose poco molto semplice e diretto, come di seguito:

Mock costruttore di una classe java.io.File:

new MockUp<File>(){ 
    @Mock 
    public void $init(String pathname){ 
     System.out.println(pathname); 
     // or do whatever you want 
    } 
}; 
  • il costruttore pubblico nome deve essere sostituito con $ init
  • argomenti ed eccezioni rimosse rimane lo stesso
  • il tipo di ritorno deve essere definito come vuoto

Mock un metodo statico:

  • rimuovere statica dal metodo firma finto
  • firma del metodo rimane lo stesso altrimenti
Problemi correlati