2011-11-08 15 views
23

Estoy tratando de burlarse de un método privado que está haciendo una llamada JNDI. Cuando se llama a ese método desde una prueba unitaria, arroja una excepción ^. Me gustaría descartar ese método para fines de prueba. Usé el sample code from another questions answer, y mientras pasa la prueba, parece que el método subyacente aún se llama. Inserté un System.err.println() en el método doTheGamble(), y se imprime en mi consola.Se burló de método privado con PowerMock, pero el método subyacente todavía se llama

Bastante interesante, si hago el comentario de la primera assertThat, la prueba pasa. ? :(

Así que, ¿cómo se burlan de un método privado por lo que no consigo llamado?

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

^como es comprensible, ya que mi espacio de trabajo no es compatible con JNDI, sólo el entorno de producción hace

% estoy usando las últimas versiones de toda la biblioteca, JUnit 4.10, Mockito 1.8.5, 1.1 Hamcrest, Javassist 3.15.0, y PowerMock 1.4.10.

Respuesta

29

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

Así que para aplicar esto a su código, creo que podría llegar a ser:

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

acabo codificadas que en el editor de aquí. No se han ejecutado pruebas, y no se han dañado los errores en la elaboración de este código.

+8

+1 para no dañar los errores. Muy cuidado de ti;) – Guillaume

+0

@Mike ¿Tienes alguna idea de por qué 'doReturn (true) .when (spy," doTheGamble ", anyString(), anyInt());' funciona pero 'doReturn (true) .when (spy , método (CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) .withArguments (anyString(), anyInt()); 'da como resultado 'IllegalArgumentException: argument type mismatch'? Este último parece más acorde con el estilo del ejemplo y al menos equivalente, pero no funciona. – ArtB

+0

Honestamente, no puedo, lo siento. Soy relativamente nuevo en Mockito/PowerMock ... Nunca intenté escribir código en ese estilo ('.withArguments (...)') antes. – Mike

0

cuando se utiliza spy para construir un objeto de burla, es un objeto con la mitad real respaldada. Mi conjetura sería ser para deshacerse de spy. Trabajar en puro objeto simulado.

+1

Estoy tratando de probar la implementación de la clase que estoy espiando así que esta no es una opción. – ArtB

1

ARTB,

¿Está seguro de que su código no funciona (o) Me estoy perdiendo algo aquí? Acabo de cambiar su método de expectativa por el siguiente camino Mike sugirió y trabaja muy bien:

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

nunca he usado Powermockito sino que se utiliza Mockito mucho antes.

+0

Sí, estoy seguro, voy a PasteBin algunos resultados una vez que llegue a trabajar. – ArtB

2

ARTB,

Sólo pegar el código completo que funciona muy bien en mi IDE de Eclipse. Solo cambié la expectativa que dije en mi última publicación. Buena suerte.

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; 
    } 
} 
Cuestiones relacionadas