Mockování a úskalí času v Javě 8
Adaptace nových verzí Javy jde pomalu. Dodnes vídám, že programátoři neumí či nechtějí používat multi catch, try-with-resources a diamond operátor. Jak chtít složitější posun, který přináší Java 8?
Ovšem sám nejsem bez viny. Java 8 už je tu víc jak dva roky. Při jejím uvedení jsem psal, jak obstojí v Akumulátor testu, ale na projektech ji naplno nevyužíváme. Rozhodl jsem se to napravit tím, že začnu používat java.time.* místo java.util.Date. Jednak kvůli API a taky proto, že jsou nové třídy immutable. Chtěl bych se podělit o to, jak jsem se při tom nachytal.
java.timex
V příkladech testování a mockování času je uvedená třída Instant, ale potřeboval jsem převod do LocalTime. Jakožto empirický programátor (to jest zkouším, co mi nadhodí kontextová nápověda) píšu:
LocalTime.from(Instant.now())
Jenže ouha
java.time.DateTimeException: Unable to obtain LocalTime from TemporalAccessor:
2016-08-22T20:40:05.875Z of type java.time.Instant
Našel jsem vysvětlení na Stack Overflow. Doporučuji k přečtení celé, ale přináším alespoň výtah.
Autoři specifikace JSR-310 nechtěli, aby lidé převáděli mezi strojovým a lidským časem přes statické metody from() v typech jako ZoneId, ZoneOffset, OffsetDateTime, ZonedDateTime atd. Pokud byste pečlivě studovali javadoc, je to tam výslovně specifikované. Místo toho použijte:
OffsetDateTime#toInstant():Instant
ZonedDateTime#toInstant():Instant
Instant#atOffset(ZoneOffset):OffsetDateTime
Instant#atZone(ZoneId):ZonedDateTimeProblém se statickými metodami from() je v tom, že jinak jsou lidé schopní převádět mezi Instant a například LocalDateTime, aniž by přemýšleli o časové zóně.
Všechny statické metody from() jsou až příliš veřejné. Podle mého názoru jsou příliš snadno přístupné a měly by být raději odstraněny z veřejného API nebo by měly používat specifičtější parametr než TemporalAccessor. Slabou stránkou těchto metod je to, že lidé můžou při konverzi zapomenout na související časovou zónu, protože začnou dotaz s lokálním typem. Zvažte například: LocalDate.from(anInstant) (v jaké časové zóně???)
Testování
Nebudu vás víc napínat teorií. Zde je příklad, kdy konfigurace je interval v hodinách (od do), přičemž na serveru běží strojový čas. Součástí příkladu je i test v Groovy.
Bean Validation
Závěrem ještě jeden zásek, proč je přechod na java.time.* složitější, a tím je chybějící podpora v Bean Validation. Jistě, můžete si validátory napsat sami, ale v Hibernate nejsou. Issue HHH-8844 se sice vztahuje k hibernate-core, ale může vysvětlit i validátor. Prostě drží zpětnou kompatibilitu k Javě 6. Ovšem svítá naděje v podobě Bean Validation 2.0