web-dev-qa-db-fra.com

Comment se moquer de la méthode privée pour tester avec PowerMock?

J'ai une classe que je voudrais tester avec une méthode publique qui appelle une méthode privée. J'aimerais supposer que la méthode privée fonctionne correctement. Par exemple, je voudrais quelque chose comme doReturn....when.... J'ai trouvé qu'il y a solution possible avec PowerMock , mais cette solution ne fonctionne pas pour moi. Comment cela peut-il être fait? Quelqu'un at-il eu ce problème?

59
Julie Langstrump

Je ne vois pas de problème ici. Avec le code suivant utilisant l'API Mockito, j'ai réussi à le faire:

public 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();
        return gamble;
    }
}

Et voici le test JUnit:

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

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

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

        spy.meaningfulPublicApi();
    }
}
92
Brice

Une solution générique qui fonctionnera avec n’importe quel framework de test ( si votre classe n’est pas -final) consiste à créer manuellement votre propre maquette.

  1. Changez votre méthode privée en protégé.
  2. Dans votre classe de test, élargissez la classe
  3. écrasez la méthode précédemment privée pour renvoyer la constante souhaitée

Cela n’utilise aucun framework, donc il n’est pas aussi élégant, mais il fonctionnera toujours: même sans PowerMock. Sinon, vous pouvez utiliser Mockito pour effectuer les étapes 2 et 3 à votre place, si vous avez déjà effectué l'étape 1.

Pour simuler directement une méthode privée, vous devez utiliser PowerMock comme indiqué dans le autre réponse .

21
Tobogganski

Pour une raison quelconque, la réponse de Brice ne fonctionne pas pour moi. J'ai été capable de le manipuler un peu pour le faire fonctionner. C'est peut-être juste parce que j'ai une version plus récente de PowerMock. J'utilise 1.6.5.

import Java.util.Random;

public 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();
        return gamble;
    }
}

La classe de test se présente comme suit:

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;

import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.doReturn;

@RunWith(PowerMockRunner.class)
@PrepareForTest(CodeWithPrivateMethod.class)
public class CodeWithPrivateMethodTest {
    private CodeWithPrivateMethod classToTest;

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

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

        classToTest.meaningfulPublicApi();
    }
}
1
gaoagong

je connais un moyen ny que vous pouvez appeler votre fonction privée pour tester en mockito

@Test
    public  void  commandEndHandlerTest() throws  Exception
    {
        Method retryClientDetail_privateMethod =yourclass.class.getDeclaredMethod("Your_function_name",null);
        retryClientDetail_privateMethod.setAccessible(true);
        retryClientDetail_privateMethod.invoke(yourclass.class, null);
    }
1
Arun Yadav