Tengo una función que utiliza el actual tiempo de hacer algunos cálculos. Me gustaría burlarse de ella mediante mockito.

Un ejemplo de la clase de la que me gustaría probar:

public class ClassToTest {
    public long getDoubleTime(){
        return new Date().getTime()*2;
    }
}

Me gustaría algo como:

@Test
public void testDoubleTime(){
   mockDateSomeHow(Date.class).when(getTime()).return(30);
   assertEquals(60,new ClassToTest().getDoubleTime());
}

Es posible burlarse de eso? No me gustaría cambiar la «prueba» de código con el fin de ser probado.

  • ¿Por qué no cambiar la prueba de código? Código que es más fácil de probar es generalmente más débilmente acoplados … ¿por qué no quieres?
  • posibles duplicados de Reemplazar el Sistema Java.currentTimeMillis
  • … y otra cosa cambiante de ‘prueba’ código es fácil – usted tiene pruebas para decir que cuando se ha cometido un error – cambio de onu-probado el código en el otro lado … necesitas Michael Plumas kung foo 😉
  • Burlarse de una nueva fecha no es una buena estrategia … si quieres otro día 🙂
  • Leer mi respuesta en stackoverflow.com/questions/11042200/…, lo cual es similar (pero no idéntico) problema.
  • Jaja! He tenido que leer tu comentario acerca de 3 veces antes de que me di cuenta de que era una broma.

InformationsquelleAutor Jordi P.S. | 2012-08-09

5 Comentarios

  1. 51

    La cosa correcta a hacer es reestructurar el código para que sea más fácil de probar como se muestra a continuación.
    La reestructuración de su código para eliminar la dependencia directa de la Fecha le permitirá inyectar diferentes implementaciones para la normal y de tiempo de ejecución de la prueba de tiempo de ejecución:

    interface DateTime {
        Date getDate();
    }
    
    class DateTimeImpl implements DateTime {
        @Override
        public Date getDate() {
           return new Date();
        }
    }
    
    class MyClass {
    
        private final DateTime dateTime;
        //inject your Mock DateTime when testing other wise inject DateTimeImpl
    
        public MyClass(final DateTime dateTime) {
            this.dateTime = dateTime;
        }
    
        public long getDoubleTime(){
            return dateTime.getDate().getTime()*2;
        }
    }
    
    public class MyClassTest {
        private MyClass myClassTest;
    
        @Before
        public void setUp() {
            final Date date = Mockito.mock(Date.class);
            Mockito.when(date.getTime()).thenReturn(30L);
    
            final DateTime dt = Mockito.mock(DateTime.class);
            Mockito.when(dt.getDate()).thenReturn(date);
    
            myClassTest = new MyClass(dt);
        }
    
        @Test
        public void someTest() {
            final long doubleTime = myClassTest.getDoubleTime();
            assertEquals(60, doubleTime);
        }
    }
    • Yo estoy de acuerdo. Hago como que todo el tiempo. Funciona muy bien, el cambio en el código original es mínima y la prueba es fácil.
    • Esta es la clásica forma de resolver este problema (lo que ustedes llaman DateTime sería más descriptivo de ser llamado Clock o algo así). Sin embargo, esto significa la reestructuración de su código y añade un poco de complejidad puramente para permitir la realización de pruebas, que es un poco de un código de olor.
    • Así lo hice, creo que es un buen planteamiento, pero la cuestión era cómo hacerlo con Mockito 😀
    • Sólo una pequeña pregunta el por qué de la Fecha de uso de la clase en lugar de Sistema.currentTimeMillis()?
    • El código no es solamente una muestra, no prod código, pero en realidad estoy usando new Date(). Por qué? Porque yo estoy trabajando con las fechas, no con Anhela. Puedo guardar una conversión de tipo. Mi db almacena las fechas (appengine almacén de datos).
    • O mejor aún : el uso de JodaTime
    • Triste la respuesta no responde a la pregunta. Si mi código código utiliza una librería que utiliza una biblioteca que utiliza new Date() y mi prueba dejado de funcionar debido a que la prueba vector que contiene un certificado válido hasta el día de ayer, no veo cómo eliminar el uso de new Date() de la biblioteca para solucionar rápidamente de mi prueba.
    • Respuestas a la pregunta original, tal como se plantea en el OP. Suena como que usted tiene una pregunta/requisito. Mejor post esta como nueva pregunta.

  2. 21

    Si tiene código de la herencia que no se puede refactorizar y usted no desea afectar System.currentTimeMillis(), intenta con el uso de Powermock y PowerMockito

    //note the static import
    import static org.powermock.api.mockito.PowerMockito.whenNew;
    
    @PrepareForTest({ LegacyClassA.class, LegacyClassB.class })
    
    @Before
    public void setUp() throws Exception {
    
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("PST"));
    
        Date NOW = sdf.parse("2015-05-23 00:00:00");
    
        //everytime we call new Date() inside a method of any class
        //declared in @PrepareForTest we will get the NOW instance 
        whenNew(Date.class).withNoArguments().thenReturn(NOW);
    
    }
    
    public class LegacyClassA {
      public Date getSomeDate() {
         return new Date(); //returns NOW
      }
    }
    • O mejor aún utilizar JMockit (que puede hacer lo que Mockito y PowerMock pueden hacer juntos), por lo que no necesitan dos diferentes pruebas framworks…
  3. 6

    Que podría hacer esto mediante el uso de PowerMock, que aumenta Mockito a ser capaz de burlarse de métodos estáticos. Entonces, usted puede simulacro de Sistema.currentTimeMillis(), que es donde new Date() finalmente llega el momento de.

    Que podría. No voy a opinar sobre si debe.

    • Estoy interesado en saber cómo hacer las cosas, ese es el objetivo de la pregunta. ¿Tienes ejemplos ?
    • Esta es la de la Powermock documentación. Debería funcionar de la misma con PowerMockito
    • Ciegamente la transformación de cada «nuevo» en un objeto contenedor y la introducción de un direccionamiento indirecto a través de una inyección de dependencia sólo hace que el código de la onu-necesariamente detallado y duro. Si se crea un objeto ArrayList o un HashMap en el interior de su clase ahora crear un ArrayListFactory o un HashMapFactory y se inyecta en su clase? A ciegas usando PowerMock en todas partes el uso de «nuevas» podría crear un acoplado muy bien el sistema. Ser capaz de decir donde herramientas como PowerMock son un buen ajuste es parte de ser un experto programador
  4. 1

    Un enfoque, que no responden directamente a la pregunta, pero podría resolver el problema subyacente (tener reproducible pruebas), es permitir que la Date como un parámetro para las pruebas y agregar un delegado a la fecha predeterminada.

    Así

    public class ClassToTest {
    
        public long getDoubleTime() {
          return getDoubleTime(new Date());
        }
    
        long getDoubleTime(Date date) {  //package visibility for tests
          return date.getTime() * 2;
        }
    }

    En el código de producción, se utiliza getDoubleTime() y prueba contra getDoubleTime(Date date).

Dejar respuesta

Please enter your comment!
Please enter your name here