EasyMock vs Mockito: diseño vs mantenibilidad? [cerrado]


Una forma de pensar sobre esto es: si nos preocupamos por el diseño del código, entonces EasyMock es la mejor opción, ya que le da retroalimentación por su concepto de expectativas.

Si nos preocupamos por el mantenimiento de las pruebas (más fáciles de leer, escribir y tener pruebas menos frágiles que no se ven afectadas mucho por el cambio), entonces Mockito parece una mejor opción.

Mis preguntas son:

  • Si ha utilizado EasyMock en proyectos a gran escala, ¿encuentra que sus pruebas son más difícil de mantener?
  • ¿Cuáles son las limitaciones del Mockito (aparte de las pruebas endo)?
Author: palacsint, 2010-05-19

5 answers

Soy un desarrollador de EasyMock, así que un poco parcial, pero por supuesto he utilizado EasyMock en proyectos a gran escala.

Mi opinión es que las pruebas de EasyMock de hecho se rompe de vez en cuando. EasyMock te obliga a hacer una grabación completa de lo que esperas. Esto requiere algo de disciplina. Realmente debe registrar lo que se espera no lo que el método probado necesita actualmente. Por ejemplo, si no importa cuántas veces se llame a un método en un simulacro, no tenga miedo de usar andStubReturn. Además, si no le importa un parámetro, use anyObject() y así sucesivamente. Pensar en TDD puede ayudar en eso.

Mi análisis es que las pruebas de EasyMock se romperán más a menudo, pero las de Mockito no lo harán cuando quieras. Prefiero que mis pruebas se rompan. Al menos soy consciente de cuáles fueron los impactos de mi desarrollo. Este es, por supuesto, mi punto de vista personal.

 29
Author: Henri,
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
2015-08-24 08:29:26

No discutiré sobre la legibilidad de prueba, el tamaño o las técnicas de prueba de estos frameworks, creo que son iguales, pero en un simple ejemplo te mostraré la diferencia.

Dado: Tenemos una clase que es responsable de almacenar algo en algún lugar:

public class Service {

    public static final String PATH = "path";
    public static final String NAME = "name";
    public static final String CONTENT = "content";
    private FileDao dao;

    public void doSomething() {
        dao.store(PATH, NAME, IOUtils.toInputStream(CONTENT));
    }

    public void setDao(FileDao dao) {
        this.dao = dao;
    }
}

Y queremos probarlo:

Mockito:

public class ServiceMockitoTest {

    private Service service;

    @Mock
    private FileDao dao;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        service = new Service();
        service.setDao(dao);
    }

    @Test
    public void testDoSomething() throws Exception {
        // given
        // when
        service.doSomething();
        // then
        ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class);
        Mockito.verify(dao, times(1)).store(eq(Service.PATH), eq(Service.NAME), captor.capture());
        assertThat(Service.CONTENT, is(IOUtils.toString(captor.getValue())));
    }
}

EasyMock:

public class ServiceEasyMockTest {
    private Service service;
    private FileDao dao;

    @Before
    public void setUp() {
        dao = EasyMock.createNiceMock(FileDao.class);
        service = new Service();
        service.setDao(dao);
    }

    @Test
    public void testDoSomething() throws Exception {
        // given
        Capture<InputStream> captured = new Capture<InputStream>();
        dao.store(eq(Service.PATH), eq(Service.NAME), capture(captured));
        replay(dao);
        // when
        service.doSomething();
        // then
        assertThat(Service.CONTENT, is(IOUtils.toString(captured.getValue())));
        verify(dao);
    }
}

Como puede ver, ambas pruebas son bastante iguales y ambas están pasando. Ahora, imaginemos que alguien más cambió de Servicio implementación y tratando de ejecutar pruebas.

Implementación de un nuevo servicio:

dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));

Se agregó el separador al final de la constante de RUTA

¿Cómo se verán los resultados de las pruebas en este momento ? En primer lugar, ambas pruebas fallarán, pero con diferentes mensajes de error:

EasyMock:

java.lang.AssertionError: Nothing captured yet
    at org.easymock.Capture.getValue(Capture.java:78)
    at ServiceEasyMockTest.testDoSomething(ServiceEasyMockTest.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

Mockito:

Argument(s) are different! Wanted:
dao.store(
    "path",
    "name",
    <Capturing argument>
);
-> at ServiceMockitoTest.testDoSomething(ServiceMockitoTest.java:34)
Actual invocation has different arguments:
dao.store(
    "path\",
    "name",
    java.io.ByteArrayInputStream@1c99159
);
-> at Service.doSomething(Service.java:13)

¿Qué sucedió en la prueba EasyMock, por qué no se capturó el resultado ? Es el método de la tienda no se ejecutó, pero espera un minuto, fue, por qué EasyMock nos miente?

Es porque EasyMock mezcla dos responsabilidades en una sola línea - stubbing y verificación. Es por eso que cuando algo está mal es difícil entender qué parte está causando el fracaso.

Por supuesto, puede decirme: simplemente cambie la prueba y mueva verificar antes de la afirmación. Wow, ¿en serio, los desarrolladores deben tener en cuenta un orden mágico inforced burlándose de marco?

Por cierto, no ayudará:

java.lang.AssertionError: 
  Expectation failure on verify:
    store("path", "name", capture(Nothing captured yet)): expected: 1, actual: 0
    at org.easymock.internal.MocksControl.verify(MocksControl.java:111)
    at org.easymock.classextension.EasyMock.verify(EasyMock.java:211)

Aún así, me está diciendo que el método no era ejecutado, pero lo fue, solo con otros parámetros.

¿Por qué Mockito es mejor ? Este marco no mezcla dos responsabilidades en un solo lugar y cuando sus pruebas fallen, comprenderá fácilmente por qué.

 97
Author: Ruslan Dzhabbarov,
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
2015-03-14 00:31:33

Si nos preocupamos por el Diseño del código, entonces Easymock es la mejor opción, ya que le da retroalimentación por su concepto de expectativas

Interesante. Descubrí que el 'concepto de expectativas' hace que muchos desarrolladores pongan más y más expectativas en las pruebas solo para satisfacer el problema de llamada de método inesperado. ¿Cómo influye en el diseño?

La prueba no debe romperse al cambiar el código. La prueba debe romperse cuando la característica deja de funcionar. Si a uno le gustan las pruebas break cuando ocurre cualquier cambio de código Sugiero escribir una prueba que confirme la suma de comprobación md5 del archivo java:)

 45
Author: Szczepan,
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-11-07 18:35:59

No creo que debas preocuparte demasiado por esto. Tanto Easymock como Mockito se pueden configurar para ser 'estricto' o 'agradable' la única diferencia es que por defecto Easymock es estricto donde Mockito es agradable.

Al igual que con todas las pruebas, no hay una regla dura y rápida, debe equilibrar la confianza de las pruebas con la capacidad de mantenimiento. Normalmente encuentro que hay ciertas áreas funcionales o técnicas que exigen un alto nivel de confianza para las que utilizaría burlas "estrictas". Por ejemplo nosotros probablemente no querría que el método debitAccount () fuera llamado más de una vez! Sin embargo, hay otros casos en los que el simulacro es realmente poco más que un trozo para que podamos probar la verdadera 'carne' del código.

En los primeros días de la compatibilidad de life API de Mockito era un problema, pero ahora hay más herramientas que soportan el framework. Powermock (un favorito personal) ahora tiene una extensión mockito

 7
Author: ,
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-28 10:03:24

Prefiero mockito para ser honesto. estado utilizando EasyMock con unitils y la combinación de ambos a menudo resulta en excepciones como IllegalArgumentException: no es una interfaz, así como MissingBehaviorExceptions. En ambos casos, aunque el código y el código de prueba están perfectamente bien. Parecía que la excepción MissingBehaviorException se debía al hecho de que los objetos burlados creados con createMock (usando classextentions!!) produjo este error. Al usar @Mock sí funcionó! No me gusta ese tipo de comportamiento engañoso y para mí eso es una clara indicación de que los desarrolladores de la misma no saben lo que están haciendo. Un buen marco siempre debe ser fácil de usar y no ambiguo. La Ilegalargumentexception también se debió a una cierta mezcla de componentes internos de EasyMock. Además, la grabación no es lo que quiero hacer. Quiero probar si mi código lanza excepciones o no y que devuelve los resultados esperados. Eso en combinación con la cobertura de código es la herramienta adecuada para mí. No quiero que mis pruebas se rompan cada vez que pongo 1 línea de código por encima o por debajo de la anterior porque eso mejora el rendimiento más o menos. Con mockito no hay problema. Con EasyMock, resultará que las pruebas fallen aunque el código no esté roto. Eso es malo. Cuesta tiempo, por lo tanto dinero. Quieres probar el comportamiento esperado. ¿De verdad te importa el orden de las cosas? Supongo que en raras ocasiones podrías. Utilice Easymock entonces. En otro caso, creo que pasará mucho menos tiempo usando mockito para escribir su prueba.

Saludos cordiales Lawrence

 5
Author: Lawrence,
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-07-10 14:39:52