2012-02-23 14 views
8

Tengo un método que implementa lógica diferente en los datos obtenidos de un DB dependiendo de cuál es la fecha actual.Lógica basada en el tiempo de pruebas unitarias en Java

Quiero probar haciendo que la unidad pruebe crear objetos, guárdelos en el DB e invoque el método probado. Sin embargo, para tener resultados predecibles, necesito cambiar la fecha del sistema cada vez y no sé cómo hacerlo en Java.

Sugerencias?

+2

cambie su método para que de hecho le dé la hora como parámetro, luego puede probar dando el tiempo que desee. ¿Qué habla contra esto? – belgther

+0

¿Puede decirnos cómo obtiene la fecha y hora actuales? Tal vez la burla funcionaría ... –

+0

Por ahora solo con 'new Date()', tal vez necesite una clase TimeProvider – Alex

Respuesta

14

Puede generar los resultados esperados usando la fecha actual.

O bien, escriba su sistema para usar una fecha/hora que le da al momento de la prueba (en lugar del reloj) De esa manera siempre es el tiempo que espera la prueba.

utilizo algo como

interface TimeSource { 
    long currentTimeMS(); // actually I have currentTimeNS 
    void currentTimeMS(long currentTimeMS); 
} 

enum VanillaTimeSource implements TimeSource { 
    INSTANCE; 

    @Override 
    public long currentTimeMS() { 
     return System.currentTimeMillis(); 
    } 

    @Override 
    public void currentTimeMS(long currentTimeMS) { 
     // ignored 
    } 
} 

class FixedTimeSource implements TimeSource { 
    private long currentTimeMS; 
    @Override 
    public long currentTimeMS() { 
     return currentTimeMS; 
    } 

    @Override 
    public void currentTimeMS(long currentTimeMS) { 
     this.currentTimeMS =    currentTimeMS; 
    } 
} 

En las pruebas de uso un FixedTimeSource que pueden ser datos impulsadas por ejemplo establecido por entradas/eventos. En producción utilizo VanillaTimeSource.INSTANCE que ignora los tiempos en las entradas/eventos y usa la hora actual.

+0

Probablemente la primera sugerencia ganó no funciona También debería haber mencionado que los datos que se obtienen exactamente también se basan en la fecha (según una determinada columna). Esto significa que a pesar de que la lógica no es diferente, tendría que generar diferentes resultados basados ​​en casi todos los días del año.Probablemente vaya por la segunda sugerencia – Alex

+0

Puede generar el resultado esperado como parte de la ejecución de la prueba, antes de verificar el resultado. No necesita generarlos por adelantado. –

+0

De hecho, crear una clase/componente que reciba el tiempo como parámetro garantiza que sea un buen componente porque se desacopla de la hora del sistema. Coloca toda la configuración específica de la aplicación ("usando la hora del sistema") fuera del componente. Solo tiene que depender de estímulos configurables externos. – helios

8

Debe considerar inyectar algo en su clase que le permita personalizar la forma en que se presenta la hora.

Por ejemplo

public interface TimeProvider { 
    DateTime getCurrentTime(); 
} 

public class UnderTest { 

    // Inject this in some way (e.g. provide it in the constructor) 
    private TimeProvider timeProvider; 

    public void MyMethod() { 
    if (timeProvider.getCurrentTime() == "1234") { 
     // Do something 
    } 
    } 
} 

Ahora en sus pruebas de unidad que puede proporcionar una aplicación falsa de proveedor de tiempo. En el código de producción real, simplemente puede devolver la hora actual.

+0

Por supuesto, también podría proporcionarla como un parámetro como 'belgther' sugiere :) –

2

Tuve un problema similar recientemente con el código que no pude refactorizar demasiado (limitaciones de tiempo, no quería romper inadvertidamente nada). Tenía un método que quería probar que llamaba System.currentTimeMillis() y el caso que quería probar dependería de lo que devolviera ese valor. Algo así como:

public class ClassINeedToTest { 
    public boolean doStuff() { 
     long l = System.currentTimeMillis(); 
     // do some calculation based on l 
     // and return the calculation 
    } 
} 

Para permitir que la unidad de pruebas, que refactorizado la clase así que tenía un método de ayuda que estaba protegida

protected long getCurrentTimeMillis() { 
    // only for unit-testing purposes 
    return System.currentTimeMillis(); 
} 

y este método fue llamado por hacerTarea(). Esto no cambia la funcionalidad pero ahora significaba que cuando lo llamo en la unidad de prueba, podría entonces anular este para devolver un valor específico, como

ClassINeedToTest testClass = new ClassINeedToTest() { 
    protected long getCurrentTimeMillis() { 
     // return specific date for my test 
     return 12456778L; 
    } 
}; 
boolean result = testClass.doStuff(); 
// test result with an assert here 

Esto significa sin embargo que he contaminado el interfaz de mi clase, por lo que puede decidir que el costo es demasiado alto. Probablemente haya mejores formas si puede refactorizar el código más.

Cuestiones relacionadas