Joda Time tem um bom DateTimeUtils.setCurrentMillisFixed () para simular o tempo.
É muito prático em testes.
Existe um equivalente na API java.time do Java 8 ?
Joda Time tem um bom DateTimeUtils.setCurrentMillisFixed () para simular o tempo.
É muito prático em testes.
Existe um equivalente na API java.time do Java 8 ?
O mais próximo é o Clock
objeto. Você pode criar um objeto Clock a qualquer hora que desejar (ou a partir da hora atual do sistema). Todos os objetos date.time têm now
métodos sobrecarregados que usam um objeto clock para a hora atual. Portanto, você pode usar injeção de dependência para injetar um Clock com um tempo específico:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
Veja Clock JavaDoc para mais detalhes
Clock.fixed
é útil em testes, enquantoClock.system
ouClock.systemUTC
pode ser usado no aplicativo.Usei uma nova classe para ocultar a
Clock.fixed
criação e simplificar os testes: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(); ... }
fonte
getClock()
método e usar o campo diretamente. Este método adiciona apenas algumas linhas de código.Eu usei um campo
private Clock clock;
e depois
no meu código de produção. Então, usei o Mockito em meus testes de unidade para simular o Clock usando Clock.fixed ():
@Mock private Clock clock; private Clock fixedClock;
Zombando:
Afirmação:
fonte
Acho que usarClock
bagunça seu código de produção.Você pode usar JMockit ou PowerMock para simular invocações de métodos estáticos em seu código de teste. Exemplo com 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); }
EDIT : Depois de ler os comentários sobre a resposta de Jon Skeet a um pergunta aqui no SO , discordo do meu eu anterior. Mais do que qualquer outra coisa, o argumento me convenceu de que você não pode paralisar os testes ao simular métodos estáticos.
Você ainda pode / deve usar simulação estática se tiver que lidar com código legado.
fonte
Eu preciso de
LocalDate
instância em vez deLocalDateTime
.Com esse motivo criei a seguinte classe de utilitário:
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); } }
E o uso dele é 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()); } }
Resultado:
2019-01-03 1998-12-12 2019-01-03
Substituído toda a criação
LocalDate.now()
deClock.getCurrentDate()
no projeto.Porque é um aplicativo de inicialização de primavera . Antes da
test
execução do perfil, basta definir uma data predefinida para todos os testes: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); } } }
E adicione a spring.factories :
fonte
Esta é uma maneira de substituir a hora do sistema atual para uma data específica para fins de teste JUnit em um aplicativo da web Java 8 com EasyMock
Joda Time com certeza é bom (obrigado Stephen, Brian, você tornou nosso mundo um lugar melhor), mas eu não tinha permissão para usá-lo.
Depois de algumas experiências, acabei descobrindo uma maneira de simular o tempo para uma data específica na API java.time do Java 8 com EasyMock
Aqui está o que precisa ser feito:
O que precisa ser feito na classe testada
Passo 1
Adicione um novo
java.time.Clock
atributo à classe testadaMyService
e certifique-se de que o novo atributo seja inicializado corretamente nos valores padrão com um bloco de instanciação ou um construtor: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 // (...) }
Passo 2
Injete o novo atributo
clock
no método que exige uma data e hora atual. Por exemplo, no meu caso tive que verificar se uma data armazenada no banco de dados havia acontecido antesLocalDateTime.now()
, a qual substituí porLocalDateTime.now(clock)
, assim: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(); } } // (...) }
O que precisa ser feito na aula de teste
etapa 3
Na classe de teste, crie um objeto de relógio simulado e injete-o na instância da classe testada pouco antes de chamar o método testado e
doExecute()
, em seguida, redefina-o de volta logo em seguida, assim: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(..) } }
Verifique no modo de depuração e você verá que a data de 3 de fevereiro de 2017 foi corretamente injetada na
myService
instância e usada na instrução de comparação e, em seguida, foi redefinida corretamente para a data atual cominitDefaultClock()
.fonte
Este exemplo mostra até como combinar Instant e LocalTime ( explicação detalhada dos problemas com a conversão )
Uma classe em teste
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); } }
Um teste 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 } }
fonte
Com a ajuda do PowerMockito para um teste de inicialização de mola, você pode simular o
ZonedDateTime
. Você precisa do seguinte.Anotações
Na aula de teste você precisa preparar o serviço que usa o
ZonedDateTime
.@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PrepareForTest({EscalationService.class}) @SpringBootTest public class TestEscalationCases { @Autowired private EscalationService escalationService; //... }
Caso de teste
No teste, você pode preparar um tempo desejado e obtê-lo em resposta à chamada do 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 }
fonte