web-dev-qa-db-fra.com

Comment mocker des méthodes statiques dans une classe avec easymock?

Supposons que j'ai une classe comme ça:

public class StaticDude{
    public static Object getGroove() {
        // ... some complex logic which returns an object
    };
}

Comment se moquer de l'appel de méthode statique en utilisant Easy Mock? StaticDude.getGroove().

J'utilise Easy Mock 3.0

29
JavaRocky

Je ne sais pas comment faire avec EasyMock pur, mais envisagez d'utiliser les extensions PowerMock d'EasyMock.

Il a beaucoup de fonctions intéressantes pour faire exactement ce dont vous avez besoin - https://github.com/jayway/powermock/wiki/MockStatic

25
Ben J

Easymock est un framework de test pour "pour les interfaces (et les objets via l'extension de classe)" afin que vous puissiez vous moquer d'une classe sans interface. Envisagez de créer un objet interfacé avec un accesseur pour votre classe statique, puis moquez cet accès à la place.

EDIT: Btw, je ne recommanderais pas de faire des classes statiques. Il est préférable d'avoir tout interfacé si vous faites du TDD.

11
stevebot

Juste au cas où PowerMock n'est pas disponible pour une raison quelconque:

Vous pouvez déplacer l'appel statique vers une méthode, remplacer cette méthode dans l'instanciation de la classe testée dans la classe de test, créer une interface locale dans la classe de test et utiliser sa méthode dans la méthode remplacée:

private interface IMocker 
{
    boolean doSomething();
}

IMocker imocker = EasyMock.createMock(IMocker.class);

...

@Override
void doSomething()
{
     imocker.doSomething();
}

...

EasyMock.expect(imocker.doSomething()).andReturn(true);
9
mocker

D'une manière générale, il n'est pas possible de se moquer d'une méthode statique sans utiliser une sorte d'accesseur, ce qui semble aller à l'encontre du but de l'utilisation d'une méthode statique. Cela peut être assez frustrant.

Il y a un outil que je connais appelé "TypeMock Isolator" qui utilise une sorte de magie satanique pour se moquer des méthodes statiques, mais cet outil est assez cher.

Le problème est que je ne connais aucun moyen de remplacer une méthode statique. Vous ne pouvez pas le déclarer virtuel. vous ne pouvez pas l'inclure dans une interface.

Désolé d'être un nelly négatif.

4

Ajout d'un exemple sur la façon d'implémenter la simulation statique le long de la simulation régulière des classes injectées avec EasyMock/PowerMock, car l'exemple lié ne montre que la simulation statique.

Et avec le PowerMockRunner les services @Mock Ne sont pas câblés sur le service @TestSubject À tester.

Disons que nous avons un service que nous voulons tester, ServiceOne:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServiceOne {

    @Autowired
    private ServiceTwo serviceTwo;

    public String methodToTest() {
        String returnServ2 = serviceTwo.methodToMock();
        return ServiceUtils.addPlus(returnServ2);
    }
}

Qui appelle un autre service dont nous voudrons nous moquer, ServiceTwo:

import org.springframework.stereotype.Service;

@Service
public class ServiceTwo {

    public String methodToMock() {
        return "ServiceTwoReturn";
    }
}

Et qui appelle une méthode statique de classe finale, ServiceUtils:

public final class ServiceUtils {

    public static String addPlus(String pParam) {
        return "+" + pParam;
    }
}

Lors de l'appel de ServiceOne.methodToTest(), nous obtenons "+ServiceTwoReturn" En retour.


Junit Test avec EasyMock, se moquant uniquement du service ServiceTwo Spring injecté:

import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;

import org.easymock.EasyMockRunner;
import org.easymock.Mock;
import org.easymock.TestSubject;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(EasyMockRunner.class)
public class ExempleTest {

    @TestSubject
    private ServiceOne serviceToTest = new ServiceOne();

    @Mock
    private ServiceTwo serviceMocked;

    @Test
    public void testMethodToTest() {
        String mockedReturn = "return2";

        expect(serviceMocked.methodToMock()).andReturn(mockedReturn);
        replay(serviceMocked);

        String result = serviceToTest.methodToTest();

        verify(serviceMocked);

        assertEquals("+" + mockedReturn, result);
    }
}

Junit Test avec EasyMock & PowerMock, se moquant du service ServiceTwo Spring injecté mais aussi de la classe finale et de sa méthode statique:

import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.easymock.PowerMock.createMock;
import static org.powermock.api.easymock.PowerMock.mockStatic;
import static org.powermock.reflect.Whitebox.setInternalState;

import org.easymock.Mock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(ServiceUtils.class)
public class ExempleTest {

    private ServiceOne serviceToTest;

    private ServiceTwo serviceMocked;

    @Before
    public void setUp() {
        serviceToTest = new ServiceOne();
        serviceMocked = createMock(ServiceTwo.class);
        // This will wire the serviced mocked into the service to test
        setInternalState(serviceToTest, serviceMocked);
        mockStatic(ServiceUtils.class);
    }

    @Test
    public void testMethodToTest() {
        String mockedReturn = "return2";
        String mockedStaticReturn = "returnStatic";

        expect(serviceMocked.methodToMock()).andReturn(mockedReturn);
        expect(ServiceUtils.addPlus(mockedReturn)).andReturn(mockedStaticReturn);
        PowerMock.replayAll();

        String result = serviceToTest.methodToTest();

        PowerMock.verifyAll();

        assertEquals(mockedStaticReturn, result);
    }
}
1
TheBakker