Mengatasi Masa Sistem untuk Ujian di Java

1. Gambaran keseluruhan

Dalam tutorial ringkas ini, kami akan memfokuskan pada cara yang berbeza untuk mengatasi masa sistem untuk ujian .

Kadang-kadang ada logik mengenai tarikh semasa dalam kod kami. Mungkin beberapa panggilan fungsi seperti Date () baru atau Calendar.getInstance () , yang akhirnya akan memanggil System.CurrentTimeMillis .

Untuk pengenalan penggunaan Java Clock , rujuk artikel ini di sini. Atau, untuk penggunaan AspectJ, di sini.

2. Menggunakan Jam di java.time

The java.time pakej dalam Java 8 termasuk kelas abstrak java.time.Clock dengan tujuan untuk membolehkan jam alternatif yang akan dipasang dan apabila diperlukan. Dengan itu, kita dapat memasang pelaksanaan kita sendiri atau mencari yang sudah dibuat untuk memenuhi keperluan kita.

Untuk mencapai tujuan kami, perpustakaan di atas merangkumi kaedah statik untuk menghasilkan pelaksanaan khas . Kami akan menggunakan dua daripadanya yang mengembalikan pelaksanaan yang tidak berubah, selamat dari benang dan bersiri.

Yang pertama adalah tetap . Dari itu, kita boleh mendapatkan Clock yang sentiasa kembali yang sama Segera , memastikan ujian tidak adalah bergantung kepada masa semasa.

Untuk menggunakannya, kami memerlukan Instant dan Zon Offset :

Instant.now(Clock.fixed( Instant.parse("2018-08-22T10:00:00Z"), ZoneOffset.UTC))

Kaedah statik kedua diimbangi . Dalam yang satu ini, jam membungkus jam yang lain yang menjadikannya objek yang dikembalikan mampu mendapatkan instan yang kemudian atau lebih awal dengan jangka masa yang ditentukan.

Dengan kata lain, mungkin untuk mensimulasikan berjalan di masa depan, pada masa lalu, atau pada titik waktu sewenang-wenangnya :

Clock constantClock = Clock.fixed(ofEpochMilli(0), ZoneId.systemDefault()); // go to the future: Clock clock = Clock.offset(constantClock, Duration.ofSeconds(10)); // rewind back with a negative value: clock = Clock.offset(constantClock, Duration.ofSeconds(-5)); // the 0 duration returns to the same clock: clock = Clock.offset(constClock, Duration.ZERO);

Dengan kelas Duration , boleh dilakukan manipulasi dari nanodetik hingga beberapa hari. Selain itu, kita boleh menolak jangka masa, yang bermaksud untuk mendapatkan salinan tempoh ini dengan panjangnya ditolak.

3. Menggunakan Pengaturcaraan Berorientasikan Aspek

Cara lain untuk mengatasi masa sistem adalah dengan AOP. Dengan pendekatan ini, kami dapat menenun kelas Sistem untuk mengembalikan nilai yang telah ditentukan yang dapat kami tetapkan dalam kes ujian kami .

Juga, mungkin untuk menenun kelas aplikasi untuk mengalihkan panggilan ke System.currentTimeMillis () atau ke Tarikh baru () ke kelas utiliti kami sendiri.

Salah satu cara untuk melaksanakannya adalah dengan penggunaan AspectJ:

public aspect ChangeCallsToCurrentTimeInMillisMethod { long around(): call(public static native long java.lang.System.currentTimeMillis()) && within(user.code.base.pckg.*) { return 0; } } 

Dalam contoh di atas, kami menangkap setiap panggilan ke System.currentTimeMillis () di dalam pakej yang ditentukan , yang dalam hal ini adalah user.code.base.pckg. * , Dan mengembalikan sifar setiap kali peristiwa ini berlaku .

Di tempat inilah kita dapat menyatakan pelaksanaan kita sendiri untuk mendapatkan masa yang diinginkan dalam milisaat.

Satu kelebihan menggunakan AspectJ adalah ia beroperasi pada tahap bytecode secara langsung, jadi ia tidak memerlukan kod sumber asal untuk berfungsi.

Untuk itu, kami tidak perlu menyusunnya semula.

4. Mocking yang Instant.now () Menghubungi

Kita boleh menggunakan kelas Instan untuk mewakili titik seketika pada garis masa. Biasanya, kita dapat menggunakannya untuk merakam cap waktu acara dalam aplikasi kita. Kaedah sekarang () dari kelas ini membolehkan kita mendapatkan segera semasa dari jam sistem di zon waktu UTC.

Mari kita lihat beberapa alternatif untuk mengubah tingkah lakunya ketika kita menguji.

4.1. Beban berlebihan sekarang () Dengan Jam

Kita boleh memuatkan kaedah sekarang () dengan contoh Jam tetap . Banyak kelas dalam pakej java.time mempunyai kaedah sekarang () yang mengambil parameter Jam , yang menjadikan ini pendekatan pilihan kami:

@Test public void givenFixedClock_whenNow_thenGetFixedInstant() { String instantExpected = "2014-12-22T10:15:30Z"; Clock clock = Clock.fixed(Instant.parse(instantExpected), ZoneId.of("UTC")); Instant instant = Instant.now(clock); assertThat(instant.toString()).isEqualTo(instantExpected); }

4.2. Menggunakan PowerMock

Sebagai tambahan, jika kita perlu mengubah tingkah laku kaedah sekarang () tanpa mengirim parameter, kita dapat menggunakan PowerMock :

@RunWith(PowerMockRunner.class) @PrepareForTest({ Instant.class }) public class InstantUnitTest { @Test public void givenInstantMock_whenNow_thenGetFixedInstant() { String instantExpected = "2014-12-22T10:15:30Z"; Clock clock = Clock.fixed(Instant.parse(instantExpected), ZoneId.of("UTC")); Instant instant = Instant.now(clock); mockStatic(Instant.class); when(Instant.now()).thenReturn(instant); Instant now = Instant.now(); assertThat(now.toString()).isEqualTo(instantExpected); } }

4.3. Menggunakan JMockit

Sebagai alternatif, kita boleh menggunakan perpustakaan JMockit .

JMockit menawarkan dua kaedah mengejek kaedah statik kepada kita. Salah satunya menggunakan kelas MockUp :

@Test public void givenInstantWithJMock_whenNow_thenGetFixedInstant() { String instantExpected = "2014-12-21T10:15:30Z"; Clock clock = Clock.fixed(Instant.parse(instantExpected), ZoneId.of("UTC")); new MockUp() { @Mock public Instant now() { return Instant.now(clock); } }; Instant now = Instant.now(); assertThat(now.toString()).isEqualTo(instantExpected); }

Dan yang lain menggunakan kelas Harapan :

@Test public void givenInstantWithExpectations_whenNow_thenGetFixedInstant() { Clock clock = Clock.fixed(Instant.parse("2014-12-23T10:15:30.00Z"), ZoneId.of("UTC")); Instant instantExpected = Instant.now(clock); new Expectations(Instant.class) { { Instant.now(); result = instantExpected; } }; Instant now = Instant.now(); assertThat(now).isEqualTo(instantExpected); }

5. Mocking yang LocalDateTime.now () Menghubungi

Kelas lain yang berguna dalam pakej java.time adalah kelas LocalDateTime . Kelas ini mewakili waktu-tarikh tanpa zon waktu dalam sistem kalendar ISO-8601. Kaedah sekarang () kelas ini membolehkan kita mendapatkan tarikh-tarikh semasa dari jam sistem di zon waktu lalai.

Kita boleh menggunakan alternatif yang sama untuk mengejeknya seperti yang kita lihat sebelumnya. Contohnya, memuatkan lebih banyak sekarang () dengan Jam tetap :

@Test public void givenFixedClock_whenNow_thenGetFixedLocalDateTime() { Clock clock = Clock.fixed(Instant.parse("2014-12-22T10:15:30.00Z"), ZoneId.of("UTC")); String dateTimeExpected = "2014-12-22T10:15:30"; LocalDateTime dateTime = LocalDateTime.now(clock); assertThat(dateTime).isEqualTo(dateTimeExpected); }

6. Kesimpulannya

Dalam artikel ini, kami telah meneroka pelbagai cara untuk mengatasi masa sistem untuk ujian. Pertama, kita melihat pakej asli java.time dan kedudukan Clock kelas. Seterusnya, kami melihat bagaimana menerapkan aspek untuk menenun kelas Sistem . Akhirnya, kami melihat alternatif yang berbeza untuk mengejek kaedah sekarang () pada kelas Instant dan LocalDateTime .

Seperti biasa, sampel kod boleh didapati di GitHub.