Joda Time tiene un buen DateTimeUtils.setCurrentMillisFixed () para simular el tiempo.
Es muy práctico en las pruebas.
¿Existe un equivalente en la API java.time de Java 8 ?
Joda Time tiene un buen DateTimeUtils.setCurrentMillisFixed () para simular el tiempo.
Es muy práctico en las pruebas.
¿Existe un equivalente en la API java.time de Java 8 ?
Lo más cercano es el Clock
objeto. Puede crear un objeto Reloj utilizando la hora que desee (o desde la hora actual del Sistema). Todos los objetos date.time tienen now
métodos sobrecargados que toman un objeto de reloj en lugar de la hora actual. Entonces puede usar la inyección de dependencia para inyectar un Reloj con una hora específica:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
Consulte Clock JavaDoc para obtener más detalles.
Clock.fixed
es útil en las pruebas, mientras queClock.system
oClock.systemUTC
podría usarse en la aplicación.Usé una nueva clase para ocultar la
Clock.fixed
creación y simplificar las pruebas:public class TimeMachine { private static Clock clock = Clock.systemDefaultZone(); private static ZoneId zoneId = ZoneId.systemDefault(); public static LocalDateTime now() { return LocalDateTime.now(getClock()); } public static void useFixedClockAt(LocalDateTime date){ clock = Clock.fixed(date.atZone(zoneId).toInstant(), zoneId); } public static void useSystemDefaultZoneClock(){ clock = Clock.systemDefaultZone(); } private static Clock getClock() { return clock ; } }
public class MyClass { public void doSomethingWithTime() { LocalDateTime now = TimeMachine.now(); ... } }
@Test public void test() { LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2); MyClass myClass = new MyClass(); TimeMachine.useFixedClockAt(twoWeeksAgo); myClass.doSomethingWithTime(); TimeMachine.useSystemDefaultZoneClock(); myClass.doSomethingWithTime(); ... }
fuente
getClock()
método y usar el campo directamente. Este método no agrega nada más que unas pocas líneas de código.Usé un campo
private Clock clock;
y entonces
en mi código de producción. Luego usé Mockito en mis pruebas unitarias para simular el Reloj usando Clock.fixed ():
@Mock private Clock clock; private Clock fixedClock;
Burlón:
Afirmación:
fuente
Encuentro el uso deClock
desorden en su código de producción.Puede usar JMockit o PowerMock para simular invocaciones de métodos estáticos en su código de prueba. Ejemplo con JMockit:
@Test public void testSth() { LocalDate today = LocalDate.of(2000, 6, 1); new Expectations(LocalDate.class) {{ LocalDate.now(); result = today; }}; Assert.assertEquals(LocalDate.now(), today); }
EDITAR : Después de leer los comentarios sobre la respuesta de Jon Skeet a una pregunta similar aquí en SO , no estoy de acuerdo con mi yo pasado. Más que cualquier otra cosa, el argumento me convenció de que no se pueden paralizar las pruebas cuando se simulan métodos estáticos.
Sin embargo, puede / debe usar la simulación estática si tiene que lidiar con el código heredado.
fuente
Necesito
LocalDate
instancia en lugar deLocalDateTime
.Con tal razón creé la siguiente clase de utilidad:
public final class Clock { private static long time; private Clock() { } public static void setCurrentDate(LocalDate date) { Clock.time = date.toEpochDay(); } public static LocalDate getCurrentDate() { return LocalDate.ofEpochDay(getDateMillis()); } public static void resetDate() { Clock.time = 0; } private static long getDateMillis() { return (time == 0 ? LocalDate.now().toEpochDay() : time); } }
Y su uso es como:
class ClockDemo { public static void main(String[] args) { System.out.println(Clock.getCurrentDate()); Clock.setCurrentDate(LocalDate.of(1998, 12, 12)); System.out.println(Clock.getCurrentDate()); Clock.resetDate(); System.out.println(Clock.getCurrentDate()); } }
Salida:
2019-01-03 1998-12-12 2019-01-03
Reemplazado toda la creación
LocalDate.now()
queClock.getCurrentDate()
en el proyecto.Porque es una aplicación de arranque de primavera . Antes de la
test
ejecución del perfil, simplemente establezca una fecha predefinida para todas las pruebas:public class TestProfileConfigurer implements ApplicationListener<ApplicationPreparedEvent> { private static final LocalDate TEST_DATE_MOCK = LocalDate.of(...); @Override public void onApplicationEvent(ApplicationPreparedEvent event) { ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment(); if (environment.acceptsProfiles(Profiles.of("test"))) { Clock.setCurrentDate(TEST_DATE_MOCK); } } }
Y agregue a spring.factories :
fuente
Esta es una forma funcional de anular la hora actual del sistema a una fecha específica para propósitos de prueba JUnit en una aplicación web Java 8 con EasyMock
Joda Time es realmente agradable (gracias Stephen, Brian, han hecho de nuestro mundo un lugar mejor) pero no se me permitió usarlo.
Después de experimentar un poco, finalmente se me ocurrió una forma de simular el tiempo para una fecha específica en la API java.time de Java 8 con EasyMock
Esto es lo que debe hacerse:
Qué se debe hacer en la clase probada
Paso 1
Agregue un nuevo
java.time.Clock
atributo a la clase probadaMyService
y asegúrese de que el nuevo atributo se inicialice correctamente en los valores predeterminados con un bloque de instanciación o un constructor:import java.time.Clock; import java.time.LocalDateTime; public class MyService { // (...) private Clock clock; public Clock getClock() { return clock; } public void setClock(Clock newClock) { clock = newClock; } public void initDefaultClock() { setClock( Clock.system( Clock.systemDefaultZone().getZone() // You can just as well use // java.util.TimeZone.getDefault().toZoneId() instead ) ); } { initDefaultClock(); } // initialisation in an instantiation block, but // it can be done in a constructor just as well // (...) }
Paso 2
Inyecte el nuevo atributo
clock
en el método que requiere una fecha y hora actual. Por ejemplo, en mi caso tuve que realizar una verificación de si una fecha almacenada en la base de datos sucedió antesLocalDateTime.now()
, que reemplacé conLocalDateTime.now(clock)
, así:import java.time.Clock; import java.time.LocalDateTime; public class MyService { // (...) protected void doExecute() { LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB(); while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) { someOtherLogic(); } } // (...) }
Qué se debe hacer en la clase de prueba
Paso 3
En la clase de prueba, cree un objeto de reloj simulado e inyéctelo en la instancia de la clase probada justo antes de llamar al método probado
doExecute()
, luego reinícielo inmediatamente después, así:import java.time.Clock; import java.time.LocalDateTime; import java.time.OffsetDateTime; import org.junit.Test; public class MyServiceTest { // (...) private int year = 2017; // Be this a specific private int month = 2; // date we need private int day = 3; // to simulate. @Test public void doExecuteTest() throws Exception { // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot MyService myService = new MyService(); Clock mockClock = Clock.fixed( LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()), Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId() ); myService.setClock(mockClock); // set it before calling the tested method myService.doExecute(); // calling tested method myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method // (...) remaining EasyMock stuff: verify(..) and assertEquals(..) } }
Compruébelo en modo de depuración y verá que la fecha del 3 de febrero de 2017 se ha inyectado correctamente en la
myService
instancia y se ha utilizado en la instrucción de comparación, y luego se ha restablecido correctamente a la fecha actual coninitDefaultClock()
.fuente
Este ejemplo incluso muestra cómo combinar Instant y LocalTime ( explicación detallada de los problemas con la conversión )
Una clase bajo prueba
import java.time.Clock; import java.time.LocalTime; public class TimeMachine { private LocalTime from = LocalTime.MIDNIGHT; private LocalTime until = LocalTime.of(6, 0); private Clock clock = Clock.systemDefaultZone(); public boolean isInInterval() { LocalTime now = LocalTime.now(clock); return now.isAfter(from) && now.isBefore(until); } }
Una prueba de Groovy
import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import java.time.Clock import java.time.Instant import static java.time.ZoneOffset.UTC import static org.junit.runners.Parameterized.Parameters @RunWith(Parameterized) class TimeMachineTest { @Parameters(name = "{0} - {2}") static data() { [ ["01:22:00", true, "in interval"], ["23:59:59", false, "before"], ["06:01:00", false, "after"], ]*.toArray() } String time boolean expected TimeMachineTest(String time, boolean expected, String testName) { this.time = time this.expected = expected } @Test void test() { TimeMachine timeMachine = new TimeMachine() timeMachine.clock = Clock.fixed(Instant.parse("2010-01-01T${time}Z"), UTC) def result = timeMachine.isInInterval() assert result == expected } }
fuente
Con la ayuda de PowerMockito para una prueba de arranque de primavera, puede burlarse del archivo
ZonedDateTime
. Necesita lo siguiente.Anotaciones
En la clase de prueba, debe preparar el servicio que utiliza el
ZonedDateTime
.@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PrepareForTest({EscalationService.class}) @SpringBootTest public class TestEscalationCases { @Autowired private EscalationService escalationService; //... }
Caso de prueba
En la prueba, puede preparar un tiempo deseado y obtenerlo en respuesta a la llamada al método.
@Test public void escalateOnMondayAt14() throws Exception { ZonedDateTime preparedTime = ZonedDateTime.now(); preparedTime = preparedTime.with(DayOfWeek.MONDAY); preparedTime = preparedTime.withHour(14); PowerMockito.mockStatic(ZonedDateTime.class); PowerMockito.when(ZonedDateTime.now(ArgumentMatchers.any(ZoneId.class))).thenReturn(preparedTime); // ... Assertions }
fuente