¿Cómo me burlo de métodos estáticos en una clase con easymock?


Supongamos que tengo una clase así:

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

¿Cómo me burlo de la llamada al método estático usando easy mock? StaticDude.getGroove().

Estoy usando easy mock 3.0

Author: Pascal Thivent, 2010-07-02

5 answers

No estoy seguro de cómo hacerlo con pure EasyMock, pero considere usar las extensiones PowerMock para EasyMock.

Tiene un montón de funciones interesantes para hacer justo lo que necesita - https://github.com/jayway/powermock/wiki/MockStatic

 25
Author: Ben J,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-25 11:08:08

Easymock es un marco de prueba para "para interfaces (y objetos a través de la extensión de clase)" para que pueda burlarse de una clase sin una interfaz. Considere la posibilidad de crear un objeto interconectado con un accesor para su clase estática y luego burlarse de ese acessor en su lugar.

EDIT: Por cierto, no recomendaría hacer clases estáticas. Es mejor tener todo interconectado si está haciendo TDD.

 11
Author: stevebot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-02 02:04:07

En caso de que PowerMock no esté disponible por cualquier motivo:

Puede mover la llamada estática a un método, anular este método en la instanciación de la clase testada en la clase test, crear una interfaz local en la clase test y usar su método en el método overidden:

private interface IMocker 
{
    boolean doSomething();
}

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

...

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

...

EasyMock.expect(imocker.doSomething()).andReturn(true);
 9
Author: mocker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-30 08:41:00

En términos generales, no es posible burlarse de un método estático sin usar algún tipo de accessor, lo que parece frustrar el propósito de usar un método estático. Puede ser bastante frustrante.

Hay una herramienta que conozco llamada "TypeMock Isolator" que usa algún tipo de Magia satánica para burlarse de métodos estáticos, pero esa herramienta es bastante cara.

El problema es que no conozco ninguna forma de anular un método estático. No puedes declararlo virtual. no se puede incluir en un interfaz.

Siento ser una nelly negativa.

 3
Author: Daniel Allen Langdon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-07 16:40:02

Agregando un ejemplo sobre cómo implementar mock estático a lo largo de mock normal de clases inyectadas con EasyMock / PowerMock, ya que el ejemplo vinculado solo muestra mock estático.

Y con el PowerMockRunner los servicios @Mock no están cableados en el servicio @TestSubject para probar.

Digamos que tenemos un servicio que queremos probar, 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);
    }
}

Que llama a otro servicio del que queremos burlarnos, ServiceTwo:

import org.springframework.stereotype.Service;

@Service
public class ServiceTwo {

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

Y que llama a un método estático de clase final, ServiceUtils:

public final class ServiceUtils {

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

Cuando se llama ServiceOne.methodToTest() obtenemos "+ServiceTwoReturn" como retorno.


Prueba Junit con EasyMock, burlándose solo del servicio inyectadotwo Spring service:

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

Prueba Junit con EasyMock y PowerMock, burlándose del servicio inyectado, pero también de la clase final y su método estático:

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
Author: TheBakker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-16 14:15:41