Joda Time memiliki DateTimeUtils.setCurrentMillisFixed () yang bagus untuk meniru waktu.
Ini sangat praktis dalam ujian.
Apakah ada yang setara di java.time API Java 8 ?
Joda Time memiliki DateTimeUtils.setCurrentMillisFixed () yang bagus untuk meniru waktu.
Ini sangat praktis dalam ujian.
Apakah ada yang setara di java.time API Java 8 ?
Jawaban:
Yang paling dekat adalah Clock
objeknya. Anda dapat membuat objek Jam menggunakan waktu yang Anda inginkan (atau dari waktu Sistem saat ini). Semua objek date.time memiliki now
metode kelebihan beban yang mengambil objek jam sebagai gantinya untuk waktu saat ini. Jadi Anda dapat menggunakan injeksi ketergantungan untuk menyuntikkan Jam dengan waktu tertentu:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
Lihat Clock JavaDoc untuk detail selengkapnya
Saya menggunakan kelas baru untuk menyembunyikan Clock.fixed
pembuatan dan menyederhanakan tes:
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()
metode tersebut dan menggunakan bidang tersebut secara langsung. Metode ini hanya menambahkan beberapa baris kode.
Saya menggunakan lapangan
private Clock clock;
lalu
LocalDate.now(clock);
dalam kode produksi saya. Lalu saya menggunakan Mockito dalam pengujian unit saya untuk mengejek Jam menggunakan Clock.fixed ():
@Mock
private Clock clock;
private Clock fixedClock;
Mengejek:
fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();
Tuntutan:
assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock)));
Saya menemukan menggunakan Clock
kekacauan kode produksi Anda.
Anda dapat menggunakan JMockit atau PowerMock untuk meniru pemanggilan metode statis dalam kode pengujian Anda. Contoh dengan 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 : Setelah membaca komentar pada jawaban Jon Skeet untuk pertanyaan serupa di sini JADI saya tidak setuju dengan diri saya di masa lalu. Lebih dari segalanya, argumen tersebut meyakinkan saya bahwa Anda tidak dapat memparalelkan pengujian ketika Anda mengejek metode statis.
Anda masih bisa / harus menggunakan ejekan statis jika Anda harus berurusan dengan kode lama.
Saya butuh LocalDate
contoh, bukan LocalDateTime
.
Dengan alasan seperti itu saya membuat kelas utilitas berikut:
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);
}
}
Dan penggunaannya seperti:
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());
}
}
Keluaran:
2019-01-03
1998-12-12
2019-01-03
Mengganti semua kreasi LocalDate.now()
ke Clock.getCurrentDate()
dalam proyek.
Karena ini adalah aplikasi boot musim semi . Sebelum test
eksekusi profil, cukup setel tanggal yang telah ditentukan untuk semua tes:
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);
}
}
}
Dan tambahkan ke spring.factories :
org.springframework.context.ApplicationListener = com.init.TestProfileConfigurer
Joda Time pasti menyenangkan (terima kasih Stephen, Brian, Anda telah membuat dunia kita menjadi tempat yang lebih baik) tetapi saya tidak diizinkan untuk menggunakannya.
Setelah beberapa percobaan, saya akhirnya menemukan cara untuk meniru waktu ke tanggal tertentu di Java.time API Java 8 dengan EasyMock
Inilah yang perlu dilakukan:
Tambahkan java.time.Clock
atribut baru ke kelas yang diuji MyService
dan pastikan atribut baru akan diinisialisasi dengan benar pada nilai default dengan blok instantiation atau konstruktor:
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
// (...)
}
Masukkan atribut baru clock
ke dalam metode yang memanggil tanggal-waktu saat ini. Misalnya, dalam kasus saya, saya harus melakukan pemeriksaan apakah tanggal yang disimpan dalam database terjadi sebelumnya LocalDateTime.now()
, yang saya ganti dengan LocalDateTime.now(clock)
, seperti:
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();
}
}
// (...)
}
Di kelas pengujian, buat objek jam tiruan dan masukkan ke dalam instance kelas yang diuji tepat sebelum Anda memanggil metode yang diuji doExecute()
, lalu setel ulang kembali setelahnya, seperti ini:
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(..)
}
}
Periksa dalam mode debug dan Anda akan melihat tanggal 3 Feb 2017 telah dimasukkan dengan benar ke dalam myService
instance dan digunakan dalam instruksi perbandingan, dan kemudian telah disetel ulang dengan benar ke tanggal saat ini dengan initDefaultClock()
.
Contoh ini bahkan menunjukkan cara menggabungkan Instan dan Waktu Lokal ( penjelasan mendetail tentang masalah dengan konversi )
Kelas yang sedang diuji
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);
}
}
Tes 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
}
}
Dengan bantuan PowerMockito untuk uji boot musim semi, Anda dapat membuat tiruan file ZonedDateTime
. Anda membutuhkan yang berikut ini.
Di kelas pengujian, Anda perlu menyiapkan layanan yang menggunakan ekstensi ZonedDateTime
.
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({EscalationService.class})
@SpringBootTest
public class TestEscalationCases {
@Autowired
private EscalationService escalationService;
//...
}
Dalam pengujian, Anda dapat menyiapkan waktu yang diinginkan, dan mendapatkannya sebagai respons dari panggilan metode.
@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
berguna dalam pengujian, sementaraClock.system
atauClock.systemUTC
mungkin digunakan dalam aplikasi.