Joda Time, alay zamanı için güzel bir DateTimeUtils.setCurrentMillisFixed () ' e sahiptir.
Testlerde çok pratik.
Java 8'in java.time API'sinde bir eşdeğeri var mı ?
Joda Time, alay zamanı için güzel bir DateTimeUtils.setCurrentMillisFixed () ' e sahiptir.
Testlerde çok pratik.
Java 8'in java.time API'sinde bir eşdeğeri var mı ?
Yanıtlar:
En yakın şey Clock
nesnedir. İstediğiniz herhangi bir zamanı kullanarak (veya Sistemin geçerli saatinden) bir Saat nesnesi oluşturabilirsiniz. Tüm date.time nesneleri now
, geçerli saat yerine bir saat nesnesi alan aşırı yüklenmiş yöntemlere sahiptir. Böylece, bir Saati belirli bir saatle enjekte etmek için bağımlılık enjeksiyonunu kullanabilirsiniz:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
Daha fazla ayrıntı için Clock JavaDoc'a bakın
Clock.fixed
Yaratılışı gizlemek ve testleri basitleştirmek için yeni bir sınıf kullandım :
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();
...
}
getClock()
yöntemi kaldırıp alanı doğrudan kullanabilirsiniz. Bu yöntem birkaç satır koddan başka bir şey eklememektedir.
Bir alan kullandım
private Clock clock;
ve sonra
LocalDate.now(clock);
üretim kodumda. Daha sonra Saat ile alay etmek için birim testlerimde Mockito kullandım Clock.fixed ():
@Mock
private Clock clock;
private Clock fixedClock;
Alaycı:
fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();
İddia:
assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock)));
Clock
Üretim kodunuzu dağınıklık kullanarak buluyorum .
Test kodunuzdaki statik yöntem çağrılarıyla alay etmek için JMockit veya PowerMock'u kullanabilirsiniz . JMockit ile örnek:
@Test
public void testSth() {
LocalDate today = LocalDate.of(2000, 6, 1);
new Expectations(LocalDate.class) {{
LocalDate.now(); result = today;
}};
Assert.assertEquals(LocalDate.now(), today);
}
DÜZENLEME : Jon Skeet'in SO'daki benzer bir soruya verdiği yanıta ilişkin yorumları okuduktan sonra geçmiş benliğime katılmıyorum. Her şeyden çok, argüman beni statik yöntemlerle dalga geçtiğinizde testleri paralize edemeyeceğiniz konusunda ikna etti.
Yine de eski kodla uğraşmak zorunda kalırsanız statik alaycı kullanabilirsiniz / kullanmalısınız.
Bunun LocalDate
yerine örneğe ihtiyacım var LocalDateTime
.
Bu nedenle aşağıdaki yardımcı sınıfını yarattım:
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);
}
}
Ve bunun için kullanım şöyle:
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());
}
}
Çıktı:
2019-01-03
1998-12-12
2019-01-03
Tüm oluşturulmasını Değiştirilen LocalDate.now()
için Clock.getCurrentDate()
projede.
Çünkü yaylı önyükleme uygulamasıdır. test
Profil yürütmeden önce tüm testler için önceden tanımlanmış bir tarih belirleyin:
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);
}
}
}
Ve bahar fabrikalarına ekleyin :
org.springframework.context.ApplicationListener = com.init.TestProfileConfigurer
Joda Time kesinlikle güzel (teşekkürler Stephen, Brian, dünyamızı daha iyi bir yer haline getirdin) ama onu kullanmama izin verilmedi.
Biraz deney yaptıktan sonra, sonunda Java 8'in java.time API'sinde EasyMock ile belirli bir tarihe zaman alay etmenin bir yolunu buldum.
İşte yapılması gerekenler:
java.time.Clock
Test edilen sınıfa yeni bir öznitelik ekleyin ve yeni özniteliğin MyService
bir örnekleme bloğu veya bir kurucu ile varsayılan değerlerde düzgün şekilde başlatıldığından emin olun:
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
// (...)
}
Yeni özniteliği clock
geçerli bir tarih-saati çağıran yönteme enjekte edin . Örneğin, benim durumumda, veritabanında depolanan bir tarihin daha önce olup olmadığını kontrol etmek zorunda kaldım ve LocalDateTime.now()
bunu şöyle değiştirdim LocalDateTime.now(clock)
:
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();
}
}
// (...)
}
Test sınıfında, bir sahte saat nesnesi oluşturun ve test edilen yöntemi çağırmadan hemen önce onu test edilen sınıfın örneğine enjekte edin doExecute()
, ardından hemen ardından sıfırlayın, şöyle:
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(..)
}
}
Hata ayıklama modunda kontrol edin ve 3 Şubat 2017 tarihinin myService
örneğe doğru şekilde enjekte edildiğini ve karşılaştırma talimatında kullanıldığını ve ardından ile geçerli tarihe uygun şekilde sıfırlandığını göreceksiniz initDefaultClock()
.
Bu örnek, Instant ve LocalTime'ın nasıl birleştirileceğini bile gösterir ( dönüştürmeyle ilgili sorunların ayrıntılı açıklaması )
Test edilen bir sınıf
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);
}
}
Harika bir test
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
}
}
Bir yay önyükleme testi için PowerMockito'nun yardımıyla ZonedDateTime
. Aşağıdakilere ihtiyacınız var.
Test sınıfının üzerinde hizmeti hazırlamanız gerekir kullanır ZonedDateTime
.
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({EscalationService.class})
@SpringBootTest
public class TestEscalationCases {
@Autowired
private EscalationService escalationService;
//...
}
Testte istediğiniz zamanı hazırlayabilir ve yöntem çağrısına yanıt olarak alabilirsiniz.
@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
}
Clock.fixed
testte faydalıdır .Clock.system
Clock.systemUTC