2010-05-19 11 views
41

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 brinda retroalimentación por su concepto de expectativas.EasyMock vs Mockito: diseño vs mantenibilidad?

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

Mis preguntas son:

  • Si ha utilizado EasyMock en proyectos a gran escala, ¿Le parece que sus pruebas son más difíciles de mantener?
  • ¿Cuáles son las limitaciones de Mockito (que no sean pruebas endo)?

Respuesta

26

Soy un desarrollador de EasyMock tan parcial pero, por supuesto, he usado EasyMock en proyectos a gran escala.

Mi opinión es que las pruebas de EasyMock se romperán 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 actualmente necesita. Por ejemplo, si no importa la cantidad de veces que se llama 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 usted lo desee. Prefiero que mis pruebas se rompan. Al menos soy consciente de cuáles fueron los impactos de mi desarrollo. Esto es, por supuesto, mi punto de vista personal.

+1

Sí, he estado pensando lo mismo: con Mockito (y Unitils Mock, una API de burla similar) es mucho más fácil de escribir pruebas que se siguen para pasar alegremente cuando no deberían hacerlo. Sospecho que esta puede ser la razón principal por la que las API tipo Mockito, que facilitan la creación de pruebas excesivamente "flojas", a menudo se consideran "más fáciles". –

+3

Me interesaría ver un ejemplo que contrasta estos 2 enfoques ... – Armand

7

No creo que deba preocuparse demasiado por esto. Tanto Easymock como Mockito se pueden configurar para ser "estrictos" o "agradables", la única diferencia es que, por defecto, Easymock es estricto cuando Mockito es agradable.

Como con todas las pruebas no existe una regla rígida, es necesario equilibrar la confianza de la prueba con el mantenimiento. Normalmente encuentro que hay ciertas áreas funcionales o técnicas que exigen un alto nivel de confianza para el cual usaría burlas "estrictas". Por ejemplo, probablemente no querríamos llamar al método debitAccount() más de una vez. Sin embargo, hay otros casos en los que el simulacro es realmente poco más que un talón, por lo que podemos probar la verdadera "carne" del código.

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

42

si nos importa el diseño del código, a continuación EasyMock es la mejor opción, ya que le da retroalimentación a usted por su concepto de expectativas

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

La prueba no se debe interrumpir cuando cambia el código. La prueba se debe interrumpir cuando la característica deja de funcionar.Si a uno le gusta que las pruebas se rompan cuando ocurre algún cambio de código, sugiero escribir una prueba que confirme la suma de comprobación md5 del archivo java :)

94

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

Teniendo en cuenta: Tenemos una clase que se encarga de almacenar algo en alguna parte:

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 pasan. Ahora, imaginemos que alguien más cambió la implementación del Servicio e intentó ejecutar las pruebas.

aplicación

nuevo servicio:

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

separador se añadió al final del PATH constante

Cómo las pruebas de resultados se verá así en este momento? En primer lugar las dos pruebas fallará, 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", 
    [email protected] 
); 
-> at Service.doSomething(Service.java:13) 

¿Qué pasó en la prueba EasyMock, qué resultado no fue capturado? ¿El método de la tienda no se ejecutó, pero espere un minuto, fue, por qué EasyMock nos miente?

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

Por supuesto que usted puede decirme - simplemente cambie la prueba y mueva la verificación antes de la aserción. Wow, ¿hablas en serio, los desarrolladores deben tener en cuenta algún orden mágico inspirado por el marco burlón?

Por cierto, no va a 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 se ha ejecutado, pero era, sólo que con otros parámetros.

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

+0

Me vendí para probar el mockito primero. El mantenimiento de la prueba es lo suficientemente complicado como para evitar que los frameworks simulados entren en el camino. – Gary

+0

Me doy cuenta de que esto es antiguo ... pero, es aquí donde se mezclan los procesos de verificación y copia aquí, no EasyMock. Es una mala práctica hacer afirmaciones antes de la verificación, por lo que puede eliminar la llamada 'assertThat' porque la verificación lo recogerá por usted: "FileStore.dao (" ruta "," nombre ", captura (Nothing Captured aún)): esperado 1, real: 0 ". Admitiré que el mensaje de Mockito es más claro aquí, pero eso no es suficiente en este ejemplo demasiado específico para influir en la decisión de alguien sobre EasyMock. –

+0

No estoy seguro de cómo he mezclado algo aquí para EasyMock, esta es la forma en que funciona, así que si tiene un buen contador de ejemplo me gustaría verlo. Creo que es una cuestión de opinión personal, ya sea que este ejemplo sea o no lo suficientemente bueno como para influir en la decisión sobre estos marcos y estoy feliz de que muchas personas lo hayan encontrado valioso. –

5

Prefiero mockito para ser sincero. He estado usando EasyMock con unidades y la combinación de ambas a menudo da como resultado excepciones como IllegalArgumentException: no una interfaz tan bien 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 simulados creados con createMock (usando classextentions !!) produjeron este error. ¡Cuando usas @Mock, funcionó!No me gusta ese tipo de comportamiento engañoso y para mí es una clara indicación de que los desarrolladores de él no saben lo que están haciendo. Un buen marco siempre debe ser fácil de usar y no ambiguo. La excepción IllegalArgumentException también se debió a algunos contactos internos de EasyMock. Además, la grabación no es lo que quiero hacer. Quiero probar si mi código arroja excepciones o no y que devuelve los resultados esperados. Eso en combinación con la cobertura del código es la herramienta correcta para mí. No quiero que se rompan mis pruebas cada vez que coloque 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, las pruebas fallarán aunque el código no esté roto. Eso es malo. Cuesta tiempo, así dinero. Desea probar el comportamiento esperado. ¿Realmente te importa el orden de las cosas? Supongo que en raras ocasiones podrías. Use Easymock entonces. En otro caso, creo que pasarás mucho menos tiempo usando mockito para escribir tus pruebas.

Saludos cordiales Lawrence

Cuestiones relacionadas