web-dev-qa-db-fra.com

Méthode privée simulée avec PowerMock, mais la méthode sous-jacente est toujours appelée

J'essaie de me moquer d'une méthode privée qui passe un appel JNDI. Lorsque cette méthode est appelée à partir d'un test unitaire, elle lève une exception ^. J'aimerais simuler cette méthode à des fins de test. J'ai utilisé le exemple de code d'une autre réponse à la question , et bien que le test soit réussi, il semble que la méthode sous-jacente soit toujours appelée. J'ai inséré une System.err.println() dans la méthode doTheGamble() et elle est imprimée sur ma console.

Assez intéressant, si je commente la première assertThat, le test réussit. ? :(

Alors, comment puis-je simuler une méthode privée pour qu'elle ne soit pas appelée?

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

^ naturellement, mon espace de travail ne prenant pas en charge JNDI, seul l’environnement de production

% J'utilise les dernières versions de toutes les bibliothèques, JUnit 4.10, Mockito 1.8.5, Hamcrest 1.1, Javassist 3.15.0 et PowerMock 1.4.10.

26
ArtB

Extrait de Exemple de méthode privée PowerMock :

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

Donc, pour appliquer cela à votre code, je pense que cela pourrait devenir:

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

Je viens de coder cela dans l'éditeur ici. Aucun test n'a été exécuté et aucun bogue n'a été causé lors de l'élaboration de ce code.

34
Mike

ArtB,

Il suffit de coller le code complet qui fonctionne bien dans mon IDE Eclipse. Je n'ai que changé l'attente que j'ai dit dans mon dernier post. Bonne chance.

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

ArtB,

Êtes-vous sûr que votre code ne fonctionne pas (ou) il me manque quelque chose ici? Je viens de remplacer votre méthode par la suivante, comme l'a suggéré Mike, et tout fonctionne bien:

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

Je n'ai jamais utilisé Powermockito, mais Mockito beaucoup auparavant.

1
skusunam

lorsque vous utilisez spy pour construire un objet fictif, c'est un véritable objet à moitié sauvegardé. Mon hypothèse serait de se débarrasser de spy. Travailler sur pure objet fantaisie. 

0
Ravi Bhatt