2012-02-02 13 views
18

Immaginate seguente codice:Mockito - spiare oggetti reali chiama il metodo originale

List list = ..... 
List spy = spy(list); 
doThrow(new NullpointerException()).when(spy).get(0); 

doThrow(....) esegue list.get(0) - questo non ha senso a tutti. Vorrei definire un comportamento simulato e non chiamare un metodo qui ..... mi manca qualcosa?

MODIFICA: L'elenco è decorato da CGLIB. Quando rimuovo il proxy CGLIB, Mockito funziona come previsto. Qualche idea su come risolvere questo problema quando si utilizzano i proxy CGLIB?

Grazie, Maciej

+0

Sembra che questa sia solo la sintassi di mockito, che utilizza il modello di builder. (come molti altri modelli di derisione) –

+0

Per quanto ne sappia Mockito, il tuo codice è corretto. La documentazione dice che 'doThrow' è solo per i metodi void, quindi puoi provare di nuovo ad es. 'clear()' per vedere se questo è il problema. Ma non riesco davvero a immaginare perché 'doThrow' non dovrebbe funzionare su metodi non void come' doReturn' fa. – flyx

+0

Non sei sicuro di cosa stai chiedendo qui. Sì, puoi usare doThrow(), anche per i metodi non void. Il motivo per cui i documenti menzionano i metodi void è perché esiste una seconda sintassi per i lanci di stub, che non funziona per i metodi void, e che viene presentata prima di quella successiva nei documenti. Il get (0) nel tuo esempio viene chiamato solo sulla spia, non sulla lista reale. La spia sa che è nel contesto di un doThrow, quindi invece di chiamare il valore REAL get (0), lo stub invece. È questo che stai chiedendo? Se è così, trasformerò questo commento in una risposta. –

risposta

6
import static org.mockito.Mockito.doThrow; 
import static org.mockito.Mockito.spy; 

import java.lang.reflect.Method; 

import org.junit.Test; 

import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 

public class MockitoSpyTest { 

    @Test 
    public void execTest() { 

     System.out.println("*** TEST 1 ***"); 
     System.out.println("Test on unmodified object"); 
     MySet ms = new MySetImpl(); 
     ms.set("test value"); 
     System.out.println("Set contains: " + ms.get()); 

     // decorate ms1 with easymock 
     System.out.println("\n*** TEST 2 ***"); 
     MySet spyMs = spy(ms); 
     doThrow(new NullPointerException("my test nullpointer")).when(spyMs).get(); 
     System.out.println("Test decorated object with SPY"); 
     spyMs.set("test value"); 
     try { 
      System.out.println("Set contains: " + spyMs.get()); 
     } catch (NullPointerException e) { 
      System.out.println("NullPointerException - as expected"); 
     } 

     // Enhance call with CGLIB 
     System.out.println("\n*** TEST 3 ***"); 
     System.out.println("Test on CGLIB decorated object"); 
     Enhancer enc = new Enhancer(); 
     enc.setSuperclass(MySetImpl.class); 
     enc.setInterfaces(new Class[] { MySet.class }); 
     enc.setCallback(new MethodInterceptor() { 

      @Override 
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
       if ("get".equals(method.getName())) { 
        System.out.println("CGLIB decorated GET call"); 
       } 
       return proxy.invokeSuper(obj, args); 
      } 
     }); 
     MySet ms1 = (MySet) enc.create(); 
     ms1.set("test value"); 
     System.out.println("Set contains: " + ms1.get()); 

     // decorate ms1 with easymock 
     System.out.println("\n*** TEST 4 ***"); 
     System.out.println("Test on CGLIB decorated object with SPY"); 
     MySet spyMs1 = spy(ms1); 
     doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get(); 
     spyMs1.set("test value"); 
     System.out.println("Set contains: " + spyMs1.get()); 
    } 

    public interface MySet { 
     void set(String val); 

     String get(); 
    } 

    public static class MySetImpl implements MySet { 
     String val; 

     public void set(String val) { 
      this.val = val; 
      System.out.println("Original SET call:" + val); 
     } 

     public String get() { 

      System.out.println("Original GET call:" + val); 
      return val; 
     } 

    } 
} 

esempio precedente produce un output:

*** TEST 1 *** 
Test on unmodified object 
Original SET call:test value 
Original GET call:test value 
Set contains: test value 

*** TEST 2 *** 
Test decorated object with SPY 
Original SET call:test value 
NullPointerException - as expected 

*** TEST 3 *** 
Test on CGLIB decorated object 
Original SET call:test value 
CGLIB decorated GET call 
Original GET call:test value 
Set contains: test value 

*** TEST 4 *** 
Test on CGLIB decorated object with SPY 
CGLIB decorated GET call 
Original GET call:test value 
Original SET call:test value 
CGLIB decorated GET call 
Original GET call:test value 
Set contains: test value 

Ora il TEST 2 e TEST 4 dovrebbe gettare NullPointerException su get chiamata - sulla base di spionaggio Mockito: doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get();

la "prova 4" non si butta previsto eccezione perché è già decorata con CGLIB - possiamo anche vedere sulla console che la chiamata CGLIb è in esecuzione: GLIB decorated GET call e non chiamare su oggetto spia. Lo stesso effetto può essere ottenuto quando si utilizza Spring AOP con i proxy CGLIB.

+0

Eccellente esempio. Tuttavia, non ho potuto vedere come questa sia diventata la risposta. Ho lo stesso problema quando utilizzo Spring AOP con Mockito. Non potrei spiare i metodi che sto spiando. – supertonsky

+0

supertonsky, credo che la risposta, in poche parole, è che non è possibile creare spie di Mockito su oggetti potenziati con CgLib. :( – bcody

0
Mockito.doThrow(new NullpointerException()).when(spy).get(0); 

Credo che il problema qui è che si sta cercando di fare un mock parziale e quindi bisogna avere l'annotazione sulla vostra classe di test:

@PrepareForTest(List.class) 

Questo potrebbe funzionare o meno. Guardando il mio codice dove cerco la gestione delle eccezioni, l'ho sempre fatto su un oggetto completamente deriso, non parzialmente deriso. Inoltre, ho fatto un uso estensivo di PowerMockito durante il parsing parziale, quindi è possibile che la libreria faccia quello che ti serve.

+0

Stai confondendo le cose. **UN.** Mockito può fare finte parziali per un po 'di tempo attraverso [Mockito # spy()] (http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#spy (T)) . Tuttavia, Mockito non prende in giro le classi finali oi metodi finali, è qui che PowerMock potrebbe diventare utile. ** B. ** Stai citando '@PrepareForTest (List.class)' che è irrilevante e sbagliato per 2 motivi: 1. List è un'interfaccia quindi non creerà problemi a prendere in giro, 2. Questa affermazione da sola ha semplicemente vinto funziona per le classi di sistema (senza aggiungere un agente). – Brice

+0

Questo potrebbe funzionare, perché il power mock genera un altro proxy. L'ho provato, e sto ricevendo un'eccezione javassis - sembra che in questo caso abbiamo conflitti tra cglib e javassis –

+4

Tu cosa non è bello? Downvoting persone mesi dopo. Se l'API cambia dopo un anno, perché punirmi per questo? –

Problemi correlati