Substitua Java System.currentTimeMillis para testar o código sensível ao tempo

129

Existe uma maneira, no código ou com argumentos da JVM, de substituir o horário atual, conforme apresentado via System.currentTimeMillis, além de alterar manualmente o relógio do sistema na máquina host?

Um pouco de fundo:

Temos um sistema que executa uma série de tarefas contábeis que giram grande parte de sua lógica em torno da data atual (ou seja, 1º do mês, 1º do ano, etc.)

Infelizmente, grande parte do código legado chama funções como new Date()ou Calendar.getInstance(), ambas eventualmente chamadas para System.currentTimeMillis.

Para fins de teste, no momento, estamos presos à atualização manual do relógio do sistema para manipular a hora e a data em que o código pensa que o teste está sendo executado.

Então, minha pergunta é:

Existe uma maneira de substituir o que é retornado System.currentTimeMillis? Por exemplo, para dizer à JVM para adicionar ou subtrair automaticamente algum deslocamento antes de retornar desse método?

Desde já, obrigado!

Mike Clark
fonte
1
Eu não sei se é mais relevante, mas não há outro método para conseguir isso com AspectJ, veja a minha resposta em: stackoverflow.com/questions/18239859/...
Nándor Előd Fekete
@ NándorElődFekete a solução no link é interessante, no entanto, é necessário recompilar o código. Eu me pergunto se o pôster original tem a capacidade de recompilar, dado o fato de que ele afirma estar lidando com código legado.
Cleberz 11/07/19
1
@cleberz Uma das boas propriedades do AspectJ é que ele opera diretamente no bytecode, portanto, não precisa do código fonte original para funcionar.
Nándor Előd Fekete 11/07
@ NándorElődFekete muito obrigado pela dica. Eu não conhecia o aspecto da instrumentação no nível de código de código (especialmente a instrumentação das classes JDK). Demorei um pouco, mas consegui descobrir que eu precisava tecer em tempo de compilação o rt.jar, bem como tecer em tempo de carregamento das classes não-jdk para atender às minhas necessidades (substituir o System. currentTimeMillis () e System.nanoTime ()).
Cleberz
@cleberz Eu tenho outra resposta para tecer nas classes JRE , você pode conferir também.
Nándor Előd Fekete

Respostas:

115

Eu recomendo fortemente que, em vez de mexer com o relógio do sistema, você morda o marcador e refatore esse código legado para usar um relógio substituível. Idealmente, isso deve ser feito com injeção de dependência, mas mesmo se você usasse um singleton substituível, obteria testabilidade.

Isso quase poderia ser automatizado com a pesquisa e substituição da versão singleton:

  • Substitua Calendar.getInstance()por Clock.getInstance().getCalendarInstance().
  • Substitua new Date()porClock.getInstance().newDate()
  • Substitua System.currentTimeMillis()porClock.getInstance().currentTimeMillis()

(etc, conforme necessário)

Depois de dar o primeiro passo, você pode substituir o singleton por DI um pouco de cada vez.

Jon Skeet
fonte
50
Organizar seu código abstraindo ou agrupando todas as APIs em potencial para aumentar a testabilidade não é uma boa idéia. Mesmo que você possa fazer a refatoração uma vez com uma simples pesquisa e substituição, o código fica muito mais difícil de ler, entender e manter. Várias estruturas simuladas devem poder modificar o comportamento de System.currentTimeMillis, caso contrário, usar AOP ou instrumentalização feita por si mesmo é a melhor escolha.
precisa saber é o seguinte
21
@jarnbjo: Bem, você é bem-vindo à sua opinião, é claro - mas essa é uma técnica IMO bem estabelecida, e eu certamente a usei com grande efeito. Além de melhorar a testabilidade, também torna explícita sua dependência da hora do sistema, o que pode ser útil ao obter uma visão geral do código.
Jon Skeet
4
@ virgo47: Acho que teremos que concordar em discordar. Não vejo "declarar explicitamente suas dependências" como poluindo o código - vejo isso como uma maneira natural de tornar o código mais claro. Nem vejo ocultar desnecessariamente essas dependências por meio de chamadas estáticas como "perfeitamente aceitáveis".
precisa
26
UPDATE O novo pacote java.time embutido no Java 8 inclui uma java.time.Clockclasse "para permitir que relógios alternativos sejam conectados conforme e quando necessário".
Basil Bourque 26/10
7
Essa resposta implica que o DI é bom. Pessoalmente, ainda estou para ver uma aplicação no mundo real que faça bom uso dela. Em vez disso, vemos uma profusão de interfaces separadas sem sentido, "objetos" sem estado, "objetos" apenas com dados e classes de baixa coesão. O IMO, a simplicidade e o design orientado a objetos (com objetos verdadeiros e com estado) são uma escolha melhor. Com relação aos staticmétodos, com certeza eles não são OO, mas injetar uma dependência sem estado cuja instância não opera em nenhum estado de instância não é realmente melhor; é apenas uma maneira elegante de disfarçar o que é efetivamente o comportamento "estático" de qualquer maneira.
Rogério
78

tl; dr

Existe uma maneira, no código ou com argumentos da JVM, de substituir o horário atual, conforme apresentado por System.currentTimeMillis, além de alterar manualmente o relógio do sistema na máquina host?

Sim.

Instant.now( 
    Clock.fixed( 
        Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
    )
)

Clock No java.time

Temos uma nova solução para o problema de uma substituição de relógio conectável para facilitar o teste com valores falsos de data e hora. O pacote java.time no Java 8 inclui uma classe abstrata java.time.Clock, com um propósito explícito:

para permitir que relógios alternativos sejam conectados como e quando necessário

Você pode conectar sua própria implementação de Clock, embora provavelmente encontre uma já feita para atender às suas necessidades. Para sua conveniência, o java.time inclui métodos estáticos para gerar implementações especiais. Essas implementações alternativas podem ser valiosas durante o teste.

Cadência alterada

Os vários tick…métodos produzem relógios que incrementam o momento atual com uma cadência diferente.

O padrão Clockrelata um horário atualizado com a frequência de milissegundos no Java 8 e no Java 9 tão bom quanto os nanossegundos (dependendo do seu hardware). Você pode solicitar que o verdadeiro momento atual seja relatado com uma granularidade diferente.

Relógios falsos

Alguns relógios podem mentir, produzindo um resultado diferente do relógio do hardware do sistema operacional host.

  • fixed - Relata um único momento imutável (sem incremento) como o momento atual.
  • offset- Relata o momento atual, mas mudou com o Durationargumento passado .

Por exemplo, prenda o primeiro momento do Natal mais cedo deste ano. em outras palavras, quando Papai Noel e suas renas fazem sua primeira parada . O fuso horário mais antigo atualmente parece estar Pacific/Kiritimatiem +14:00.

LocalDate ld = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate xmasThisYear = MonthDay.of( Month.DECEMBER , 25 ).atYear( ld.getYear() );
ZoneId earliestXmasZone = ZoneId.of( "Pacific/Kiritimati" ) ;
ZonedDateTime zdtEarliestXmasThisYear = xmasThisYear.atStartOfDay( earliestXmasZone );
Instant instantEarliestXmasThisYear = zdtEarliestXmasThisYear.toInstant();
Clock clockEarliestXmasThisYear = Clock.fixed( instantEarliestXmasThisYear , earliestXmasZone );

Use esse relógio fixo especial para sempre retornar o mesmo momento. Chegamos ao primeiro momento do dia de Natal em Kiritimati , com o UTC mostrando um relógio de parede de catorze horas antes, às 10h da data anterior de 24 de dezembro.

Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );

instant.toString (): 2016-12-24T10: 00: 00Z

zdt.toString (): 25-12-2016T00: 00 + 14: 00 [Pacific / Kiritimati]

Veja o código ao vivo em IdeOne.com .

Hora verdadeira, fuso horário diferente

Você pode controlar qual fuso horário é atribuído pela Clockimplementação. Isso pode ser útil em alguns testes. Mas eu não recomendo isso no código de produção, onde você sempre deve especificar explicitamente o opcional ZoneIdou ZoneOffsetargumentos.

Você pode especificar que o UTC seja a zona padrão.

ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );

Você pode especificar qualquer fuso horário específico. Especifique um nome próprio fuso horário no formato continent/region, como America/Montreal, Africa/Casablanca, ou Pacific/Auckland. Nunca utilize o 3-4 letras, como ESTou ISTcomo eles são não zonas verdadeiros tempo, não padronizados, e nem mesmo única (!).

ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );

É possível especificar o fuso horário padrão atual da JVM deve ser o padrão para um Clockobjeto específico .

ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );

Execute este código para comparar. Observe que todos relatam o mesmo momento, o mesmo ponto na linha do tempo. Eles diferem apenas no tempo do relógio de parede ; em outras palavras, três maneiras de dizer a mesma coisa, três maneiras de exibir o mesmo momento.

System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );

America/Los_Angeles era a zona padrão atual da JVM no computador que executou esse código.

zdtClockSystemUTC.toString (): 2016-12-31T20: 52: 39.688Z

zdtClockSystem.toString (): 2016-12-31T15: 52: 39.750-05: 00 [America / Montreal]

zdtClockSystemDefaultZone.toString (): 2016-12-31T12: 52: 39.762-08: 00 [America / Los_Angeles]

A Instantclasse está sempre no UTC, por definição. Portanto, esses três Clockusos relacionados à zona têm exatamente o mesmo efeito.

Instant instantClockSystemUTC = Instant.now ( Clock.systemUTC () );
Instant instantClockSystem = Instant.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
Instant instantClockSystemDefaultZone = Instant.now ( Clock.systemDefaultZone () );

instantClockSystemUTC.toString (): 2016-12-31T20: 52: 39.763Z

instantClockSystem.toString (): 2016-12-31T20: 52: 39.763Z

instantClockSystemDefaultZone.toString (): 2016-12-31T20: 52: 39.763Z

Relógio padrão

A implementação usada por padrão para Instant.nowé a retornada por Clock.systemUTC(). Esta é a implementação usada quando você não especifica a Clock. Veja você mesmo no código-fonte Java 9 de pré-lançamento paraInstant.now .

public static Instant now() {
    return Clock.systemUTC().instant();
}

O padrão Clockpara OffsetDateTime.nowe ZonedDateTime.nowé Clock.systemDefaultZone(). Veja o código fonte .

public static ZonedDateTime now() {
    return now(Clock.systemDefaultZone());
}

O comportamento das implementações padrão mudou entre o Java 8 e o Java 9. No Java 8, o momento atual é capturado com uma resolução apenas em milissegundos, apesar da capacidade das classes de armazenar uma resolução de nanossegundos . O Java 9 traz uma nova implementação capaz de capturar o momento atual com uma resolução de nanossegundos - dependendo, é claro, da capacidade do relógio do hardware do computador.


Sobre java.time

A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, e SimpleDateFormat.

Para saber mais, consulte o Tutorial do Oracle . E procure Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para as classes java.time .

Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de strings, não há necessidade de java.sql.*classes. O Hibernate 5 e o JPA 2.2 suportam java.time .

Onde obter as classes java.time?

Basil Bourque
fonte
esta é a resposta certa
bharal
42

Como disse Jon Skeet :

"use Joda Time" é quase sempre a melhor resposta para qualquer pergunta que envolva "como faço para obter o X com java.util.Date/Calendar?"

Então aqui vai (presumindo que você acabou substituído todo o seu new Date()com new DateTime().toDate())

//Change to specific time
DateTimeUtils.setCurrentMillisFixed(millis);
//or set the clock to be a difference from system time
DateTimeUtils.setCurrentMillisOffset(millis);
//Reset to system time
DateTimeUtils.setCurrentMillisSystem();

Se você deseja importar uma biblioteca que possui uma interface (consulte o comentário de Jon abaixo), basta usar o Relógio do Prevayler , que fornecerá implementações e também a interface padrão. A jarra cheia tem apenas 96kB, portanto não deve custar muito ...

Stephen
fonte
13
Não, eu não recomendaria isso - ele não leva você a um relógio genuinamente substituível; mantém você usando estática por toda parte. Usar o Joda Time é obviamente uma boa ideia, mas eu ainda gostaria de usar uma interface do Clock ou similar.
precisa saber é o seguinte
@ Jon: Verdade - para testar, está tudo bem, mas é melhor ter uma interface.
Stephen
5
@ Jon: Esta é uma maneira suave e perfeita e fácil e IMHO se você quiser testar o código dependente do tempo que já usa o JodaTime. Tentar reinventar a roda introduzindo outra camada / estrutura de abstração não é o caminho a seguir, se tudo já tiver sido resolvido com o JodaTime para você.
Stefan Haberl
2
@ JonSkeet: Isso não é o que Mike estava pedindo originalmente. Ele queria uma ferramenta para testar o código dependente do tempo e não um relógio substituível completo no código de produção. Prefiro manter minha arquitetura o mais complexa possível, mas o mais simples possível. Introduzindo uma camada adicional de abstracção (ou seja, interface) aqui simplesmente não é necessário
Stefan Haberl
2
@StefanHaberl: Há muitas coisas que não são estritamente "necessárias" - mas realmente o que estou sugerindo é tornar explícita uma dependência já presente e mais facilmente testável isoladamente. Fingir o tempo do sistema com estática tem todos os problemas normais da estática - é o estado global sem uma boa razão . Mike estava pedindo uma maneira de escrever código testável que usasse a data e hora atuais. Ainda acredito que minha sugestão é muito mais limpa (e torna as dependências mais claras) do que efetivamente falsificar uma estática.
22812 Jon Skeet
15

Embora o uso de algum padrão DateFactory pareça bom, ele não cobre as bibliotecas que você não pode controlar - imagine a anotação Validation @Past com a implementação baseada em System.currentTimeMillis (existe).

É por isso que usamos o jmockit para zombar da hora do sistema diretamente:

import mockit.Mock;
import mockit.MockClass;
...
@MockClass(realClass = System.class)
public static class SystemMock {
    /**
     * Fake current time millis returns value modified by required offset.
     *
     * @return fake "current" millis
     */
    @Mock
    public static long currentTimeMillis() {
        return INIT_MILLIS + offset + millisSinceClassInit();
    }
}

Mockit.setUpMock(SystemMock.class);

Como não é possível obter o valor desmembrado original de millis, usamos o nano timer - isso não está relacionado ao relógio de parede, mas o tempo relativo é suficiente aqui:

// runs before the mock is applied
private static final long INIT_MILLIS = System.currentTimeMillis();
private static final long INIT_NANOS = System.nanoTime();

private static long millisSinceClassInit() {
    return (System.nanoTime() - INIT_NANOS) / 1000000;
}

Há um problema documentado: com o HotSpot, o tempo volta ao normal após várias chamadas - eis o relatório do problema: http://code.google.com/p/jmockit/issues/detail?id=43

Para superar isso, precisamos ativar uma otimização específica do HotSpot - execute a JVM com esse argumento -XX:-Inline.

Embora isso possa não ser perfeito para produção, é adequado para testes e é absolutamente transparente para aplicativos, especialmente quando o DataFactory não faz sentido para os negócios e é introduzido apenas devido a testes. Seria bom ter a opção JVM embutida para ser executada em um tempo diferente. Pena que isso não é possível sem hacks como esse.

A história completa está no meu blog aqui: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/

A classe prática completa SystemTimeShifter é fornecida na postagem. A classe pode ser usada em seus testes, ou pode ser usada como a primeira classe principal antes da sua classe principal real com muita facilidade, a fim de executar seu aplicativo (ou mesmo todo o servidor de aplicativos) em um momento diferente. Obviamente, isso é destinado principalmente para fins de teste, não para o ambiente de produção.

EDIT Julho de 2014: O JMockit mudou muito recentemente e você deve usar o JMockit 1.0 para usá-lo corretamente (IIRC). Definitivamente não é possível atualizar para a versão mais recente, onde a interface é completamente diferente. Eu estava pensando em incluir apenas as coisas necessárias, mas como não precisamos disso em nossos novos projetos, não estou desenvolvendo nada.

virgo47
fonte
1
O fato de você estar alterando o tempo de cada classe é de duas maneiras ... por exemplo, se você zombasse do nanoTime em vez do currentTimeMillis, teria que ter muito cuidado para não quebrar o java.util.concurrent. Como regra geral, se uma biblioteca de terceiros for difícil de usar em testes, você não deve zombar das entradas da biblioteca: você deve zombar da própria biblioteca.
Dan Berindei 31/01
1
Usamos essa técnica para testes em que não zombamos de mais nada. É um teste de integração e testamos toda a pilha se ela faz o que deveria quando alguma atividade acontece pela primeira vez no ano, etc. Bom ponto sobre o nanoTime, felizmente, não precisamos zombar dele, pois não possui relógio de parede significado em tudo. Eu adoraria ver o Java com recursos de mudança de hora, pois eu precisava deles mais de uma vez e mexer com a hora do sistema por causa disso é muito azarado.
Virgo47
7

Powermock funciona muito bem. Apenas usei para zombar System.currentTimeMillis().

ReneS
fonte
Tentei usar o Powermock e, se entendi direito, não zombaria do lado do chamado. Em vez disso, encontra todos os chamadores e aplica a simulação. Isso pode ser extremamente demorado, pois é necessário deixá-lo verificar TODAS as classes em seu caminho de classe, se você quiser ter certeza de que seu aplicativo funcione em horários alternados. Corrija-me se eu estiver errado sobre a maneira de zombar do Powermock.
Virgo47
1
@ virgo47 Não, você entendeu errado. O PowerMock nunca "verifica todas as classes no seu caminho de classe"; ele verificará apenas as classes que você solicita especificamente para verificar (por meio da @PrepareForTestanotação na classe de teste).
Rogério
1
@ Rogério - é exatamente assim que eu entendo - eu disse "você tem que deixar ...". Ou seja, se você espera ligar para System.currentTimeMillisqualquer lugar do seu caminho de classe (qualquer lib), você deve verificar todas as classes. Foi isso que eu quis dizer, nada mais. O ponto é que você zomba desse comportamento no lado do chamador ("você diz especificamente"). Isso é bom para testes simples, mas não para testes em que você não pode ter certeza do que chama esse método de onde (por exemplo, testes de componentes mais complexos com as bibliotecas envolvidas). Isso não significa que o Powermock esteja errado, apenas significa que você não pode usá-lo para esse tipo de teste.
precisa
1
@ virgo47 Sim, entendo o que você quer dizer. Eu pensei que você quisesse dizer que o PowerMock teria que examinar automaticamente todas as classes no caminho de classe, o que obviamente seria absurdo. Na prática, porém, para testes de unidade, geralmente sabemos quais classes lêem o tempo, portanto, não é um problema especificá-lo; para testes de integração, de fato, o requisito no PowerMock de ter todas as classes especificadas pode ser um problema.
Rogério
6

Use Programação Orientada a Aspectos (AOP, por exemplo, AspectJ) para criar a classe System para retornar um valor predefinido que você pode definir em seus casos de teste.

Ou tecer as classes de aplicativos para redirecionar a chamada para System.currentTimeMillis()ou new Date()para outra classe de utilidade de sua preferência.

A tecelagem de classes do sistema ( java.lang.*) é um pouco mais complicada e você pode precisar executar tecelagem offline para o rt.jar e usar um JDK / rt.jar separado para seus testes.

É chamado de tecelagem binária e também existem ferramentas especiais para executar a tecelagem de classes do sistema e contornar alguns problemas com isso (por exemplo, inicializar a VM pode não funcionar)

mhaller
fonte
Isso funciona, é claro, mas é muito mais fácil fazer com uma ferramenta de zombaria do que com uma ferramenta de AOP; o último tipo de ferramenta é genérico demais para o objetivo em questão.
Rogério
3

Realmente não há uma maneira de fazer isso diretamente na VM, mas você pode definir algo programaticamente a hora do sistema na máquina de teste. A maioria (todos?) Do SO possui comandos de linha de comando para fazer isso.

Jeremy Raymond
fonte
Por exemplo, nas janelas os comandos datee time. No Linux, o datecomando
Jeremy Raymond
Por que não deveria ser possível fazer isso com AOP ou instrumentalização (isso já foi feito)?
precisa saber é o seguinte
3

Uma maneira de substituir a hora atual do sistema para fins de teste JUnit em um aplicativo da web Java 8 com EasyMock, sem Joda Time e sem PowerMock.

Aqui está o que você precisa fazer:

O que precisa ser feito na classe testada

Passo 1

Adicione um novo java.time.Clockatributo à classe testada MyServicee verifique se o novo atributo será inicializado corretamente com os 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 clockno método que solicita uma data e hora atuais. Por exemplo, no meu caso, tive que verificar se uma data armazenada no banco de dados havia acontecido antes LocalDateTime.now(), com a qual substituí LocalDateTime.now(clock), da seguinte forma:

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 antes de chamar o método testado e doExecute(), em seguida, redefina-o imediatamente depois, da seguinte forma:

import java.time.Clock;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import org.junit.Test;

public class MyServiceTest {
  // (...)
  private int year = 2017;
  private int month = 2;
  private int day = 3;

  @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 injetada corretamente na myServiceinstância e usada na instrução de comparação e, em seguida, foi redefinida corretamente para a data atual com initDefaultClock().

KiriSakow
fonte
2

Na minha opinião, apenas uma solução não invasiva pode funcionar. Especialmente se você tiver bibliotecas externas e uma grande base de códigos herdada, não há uma maneira confiável de zombar do tempo.

JMockit ... funciona apenas por um número restrito de vezes

O PowerMock & Co ... precisa simular os clientes para System.currentTimeMillis (). Novamente uma opção invasiva.

A partir disso, vejo apenas a abordagem javaagent ou aop mencionada como transparente para todo o sistema. Alguém já fez isso e poderia apontar para uma solução desse tipo?

@jarnbjo: você poderia mostrar um pouco do código javaagent, por favor?

user1477398
fonte
3
solução jmockit trabalhando por um número ilimitado de vezes com um pouco de -XX: -Hack online (no HotSpot da Sun): virgo47.wordpress.com/2012/06/22/changing-system-time-in-java Classe prática completa SystemTimeShifter no post. A classe pode ser usada em seus testes ou pode ser usada como a primeira classe principal antes da sua classe principal real com muita facilidade para executar seu aplicativo (ou mesmo todo o servidor de aplicativos) em um horário diferente. Obviamente, isso é destinado principalmente para fins de teste, não para o ambiente de produção.
Virgo47 02/07/2012
2

Se você estiver executando o Linux, poderá usar a ramificação principal do libfaketime ou no momento do teste do commit 4ce2835 .

Simplesmente defina a variável de ambiente com o tempo que você gostaria de zombar do seu aplicativo java e execute-o usando ld-preloading:

# bash
export FAKETIME="1985-10-26 01:21:00"
export DONT_FAKE_MONOTONIC=1
LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1 java -jar myapp.jar

A segunda variável de ambiente é fundamental para aplicativos java, que de outra forma seriam congelados. Requer o ramo principal do libfaketime no momento da redação.

Se você quiser alterar o horário de um serviço gerenciado pelo systemd, adicione o seguinte às substituições de arquivos da sua unidade, por exemplo, para elasticsearch, isso seria /etc/systemd/system/elasticsearch.service.d/override.conf:

[Service]
Environment="FAKETIME=2017-10-31 23:00:00"
Environment="DONT_FAKE_MONOTONIC=1"
Environment="LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1"

Não se esqueça de recarregar o systemd usando o `systemctl daemon-reload

faxmodem
fonte
-1

Se você quiser zombar do método que tem System.currentTimeMillis()argumento, pode passar anyLong()da classe Matchers como argumento.

PS Consigo executar meu caso de teste com sucesso usando o truque acima e apenas para compartilhar mais detalhes sobre o meu teste que estou usando as estruturas PowerMock e Mockito.

Paresh Mayani
fonte