2011-11-08 15 views
23

Sto provando a prendere in giro per prendere in giro un metodo privato che sta effettuando una chiamata JNDI. Quando quel metodo viene chiamato da un test unitario, genera un'eccezione ^. Mi piacerebbe prendere in giro quel metodo per scopi di test. Ho usato lo sample code from another questions answer e, sebbene il test passi, sembra che il metodo sottostante venga ancora chiamato. Ho inserito uno System.err.println() nel metodo doTheGamble() e viene stampato sulla mia console.Metodo privato con Mocked con PowerMock, ma il metodo sottostante viene ancora chiamato

Interessante, se commento il primo assertThat, il test ha esito positivo. ? :(

Allora, come faccio a mock di un metodo privato in modo che non ottiene chiamato?

import static org.hamcrest.core.Is.is; 
import static org.junit.Assert.assertThat; 
import static org.mockito.Matchers.anyInt; 
import static org.mockito.Matchers.anyString; 
import static org.powermock.api.mockito.PowerMockito.when; 
import static org.powermock.api.support.membermodification.MemberMatcher.method; 

import java.util.Random; 

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

@RunWith(PowerMockRunner.class) 
@PrepareForTest(CodeWithPrivateMethod.class) 
public class PowerMock_Test { 

    static boolean gambleCalled = false; 

    @Test(expected = RuntimeException.class) 
    public void when_gambling_is_true_then_always_explode() throws Exception { 
     CodeWithPrivateMethod spy = PowerMockito.spy(new CodeWithPrivateMethod()); 

     when(spy, method(CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) 
       .withArguments(anyString(), anyInt()) 
       .thenReturn(true); 

/* 1 */ assertThat(PowerMock_Test.gambleCalled, is(false)); 
     spy.meaningfulPublicApi(); 
/* 2 */ assertThat(PowerMock_Test.gambleCalled, is(false)); 
    } 
} 


class CodeWithPrivateMethod { 

    public void meaningfulPublicApi() { 
     if (doTheGamble("Whatever", 1 << 3)) { 
      throw new RuntimeException("boom"); 
     } 
    } 

    private boolean doTheGamble(String whatever, int binary) { 
     Random random = new Random(System.nanoTime()); 
     boolean gamble = random.nextBoolean(); 

     System.err.println("\n>>> GAMBLE CALLED <<<\n"); 
     PowerMock_Test.gambleCalled = true; 

     return gamble; 
    } 
} 

^comprensibilmente, dato che il mio lavoro non supporta JNDI, solo l'ambiente di produzione fa

% sto usando le ultime versioni di tutti i biblioteca, JUnit 4.10, Mockito 1.8.5, Hamcrest 1.1, Javassist 3.15.0, e PowerMock 1.4.10.

risposta

29

Dal PowerMock Private Method Example:

@RunWith(PowerMockRunner.class) 
// We prepare PartialMockClass for test because it's final or we need to mock private or static methods 
@PrepareForTest(PartialMockClass.class) 
public class YourTestCase { 
@Test 
public void privatePartialMockingWithPowerMock() {   
    PartialMockClass classUnderTest = PowerMockito.spy(new PartialMockClass()); 

    // use PowerMockito to set up your expectation 
    PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1"); 

    // execute your test 
    classUnderTest.execute(); 

    // Use PowerMockito.verify() to verify result 
    PowerMockito.verifyPrivate(classUnderTest, times(2)).invoke("methodToMock", "parameter1"); 
} 

Quindi, per applicare questo al vostro codice, penso che potrebbe diventare:

@RunWith(PowerMockRunner.class) 
@PrepareForTest(CodeWithPrivateMethod.class) 
public class PowerMock_Test { 
    @Test(expected = RuntimeException.class) 
    public void when_gambling_is_true_then_always_explode() throws Exception { 
     CodeWithPrivateMethod spy = PowerMockito.spy(new CodeWithPrivateMethod()); 

     PowerMockito.doReturn(true).when(spy, "doTheGamble", anyString(), anyInt()); 


/* 1 */ PowerMockito.verifyPrivate(spy, times(0)).invoke("doTheGamble", anyString(), anyInt());    
     spy.meaningfulPublicApi(); 
/* 2 */ PowerMockito.verifyPrivate(spy, times(2)).invoke("doTheGamble", anyString(), anyInt());    
    } 
} 

Ho appena codificato che nell'editor qui. Nessun test è stato effettivamente eseguito e nessun bug è stato danneggiato nella creazione di questo codice.

+8

+1 per non danneggiare i bug. Molto premuroso di te;) – Guillaume

+0

@Mike Hai idea del motivo 'doReturn (true) .when (spy," doTheGamble ", anyString(), anyInt());' funziona ma 'doReturn (true) .when (spia , method (CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) .withArguments (anyString(), anyInt()); 'restituisce un 'IllegalArgumentException: tipo di argomento non corrispondente'? Quest'ultimo sembra più in linea con lo stile dell'esempio e almeno equivalente ma non funziona. – ArtB

+0

Onestamente non posso, mi dispiace. Sono relativamente nuovo a Mockito/PowerMock stesso ... Non ho mai provato a scrivere codice in quello stile ('.withArguments (...)') prima. – Mike

0

quando si utilizza spy di costruire un oggetto fittizio, è un vero oggetto mezzo appoggiato. La mia ipotesi lo farebbe be to get of spy. Lavorare su puro oggetto simulato.

+1

Sto provando a testare l'implementazione della classe che sto spiando quindi questa non è un'opzione. – ArtB

1

ArtB,

Sei sicuro il codice non funziona (o) mi sto perdendo qualcosa qui? Ho appena sostituito il metodo di aspettativa con la seguente il modo in cui Mike ha suggerito e funziona benissimo:

PowerMockito.doReturn(true).when(spy, 
       method(CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) 
       .withArguments(anyString(), anyInt()); 

ho mai usato ma Powermockito usato Mockito molto prima.

+0

Sì, ne sono sicuro, lo farò. Una volta arrivato al lavoro, recupererò un po 'di output. – ArtB

2

ArtB,

Basta incollare il codice completo, che funziona bene nel mio Eclipse IDE. Ho solo cambiato le aspettative che ho detto nel mio ultimo post. In bocca al lupo.

import static org.hamcrest.core.Is.is; 
import static org.junit.Assert.assertThat; 
import static org.mockito.Matchers.anyInt; 
import static org.mockito.Matchers.anyString; 
import static org.powermock.api.support.membermodification.MemberMatcher.method; 

import java.util.Random; 

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

@RunWith(PowerMockRunner.class) 
@PrepareForTest(CodeWithPrivateMethod.class) 
public class PowerMock_Test { 

    static boolean gambleCalled = false; 

    @Test(expected = RuntimeException.class) 
    public void when_gambling_is_true_then_always_explode() throws Exception { 
     CodeWithPrivateMethod spy = PowerMockito.spy(new CodeWithPrivateMethod()); 

//  PowerMockito.doReturn(true).when(spy, "doTheGamble", anyString(), anyInt()); 

     PowerMockito.doReturn(true).when(spy, 
       method(CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) 
       .withArguments(anyString(), anyInt()); 

     assertThat(PowerMock_Test.gambleCalled, is(false)); 
     spy.meaningfulPublicApi(); 
     assertThat(PowerMock_Test.gambleCalled, is(false)); 
    } 
} 


class CodeWithPrivateMethod { 

    public void meaningfulPublicApi() { 
     if (doTheGamble("Whatever", 1 << 3)) { 
      throw new RuntimeException("boom"); 
     } 
    } 

    private boolean doTheGamble(String whatever, int binary) { 
     Random random = new Random(System.nanoTime()); 
     boolean gamble = random.nextBoolean(); 

     System.err.println("\n>>> GAMBLE CALLED <<<\n"); 
     PowerMock_Test.gambleCalled = true; 

     return gamble; 
    } 
} 
Problemi correlati