Перевірити Java System.currentTimeMillis для тестування коду, який відрізняється часом


129

Чи є спосіб, або в коді, або з аргументами JVM, переосмислити поточний час, представлений через System.currentTimeMillis, за винятком ручної зміни системного годинника на хост-машині?

Невеликий фон:

У нас є система, яка виконує ряд завдань бухгалтерського обліку, які обертають велику частину своєї логіки приблизно на поточну дату (наприклад, 1-го числа місяця, 1-го року тощо)

На жаль, багато застарілих кодів викликає такі функції, як, new Date()або Calendar.getInstance()обидві, в кінцевому підсумку викликають до System.currentTimeMillis.

З метою тестування зараз ми затримуємось оновленням системного годинника вручну, щоб маніпулювати часом та датою коду, який вважає, що тест виконується.

Отже, моє питання:

Чи є спосіб змінити те, що повертається System.currentTimeMillis ? Наприклад, сказати JVM автоматично додати або відняти деякий зміщення перед поверненням із цього методу?

Спасибі заздалегідь!


1
Я не знаю , чи є це відношення більше, але є ще один спосіб для досягнення цієї мети з AspectJ, побачити моя відповідь на: stackoverflow.com/questions/18239859 / ...
Nándor Előd Fekete

@ NándorElődFekete рішення у посиланні цікаве, проте воно потребує перекомпіляції коду. Цікаво, чи має оригінальний плакат можливість перекомпілювати, враховуючи той факт, що він стверджує, що має справу зі застарілим кодом.
cleberz

1
@cleberz Одне з приємних властивостей AspectJ полягає в тому, що він працює безпосередньо на байт-коді, тому для роботи йому не потрібен вихідний вихідний код.
Nándor Előd Fekete

@ NándorElődFekete дуже дякую за підказку. Мені не було відомо про приладдя на рівні байт-коду аспектуJ (особливо інструментальне обладнання класів JDK). Ми зайняли деякий час, але я зміг зрозуміти, що мені потрібно було як плетіння часу компіляції rt.jar, так і плетіння часу навантаження не jdk класів, щоб відповідати моїм потребам (переосмислити систему. currentTimeMillis () та System.nanoTime ()).
cleberz

@cleberz У мене є ще одна відповідь на плетіння через класи JRE , ви також можете це перевірити.
Nándor Előd Fekete

Відповіді:


115

Я настійно рекомендую , що замість того , щоб возитися з системним годинником, ви зціпити зуби і рефакторинг , що успадкований код для використання змінних годин. В ідеалі це потрібно робити з ін'єкцією залежності, але навіть якщо ви використовували змінний сингл, ви отримаєте перевірку.

Це майже може бути автоматизовано за допомогою пошуку та заміни для однотонної версії:

  • Замініть Calendar.getInstance()на Clock.getInstance().getCalendarInstance().
  • Замініть new Date()наClock.getInstance().newDate()
  • Замініть System.currentTimeMillis()наClock.getInstance().currentTimeMillis()

(тощо за потребою)

Після того, як ви зробили цей перший крок, ви зможете одночасно замінити сингл на DI.


50
Збивання коду шляхом абстрагування або загортання всіх потенційних API, щоб підвищити перевірку, IMHO - не дуже гарна ідея. Навіть якщо ви можете зробити рефакторинг один раз за допомогою простого пошуку та заміни, код стає набагато складніше читати, розуміти та підтримувати. Кілька макетних фреймворків повинні мати можливість змінювати поведінку System.currentTimeMillis, якщо ні, то кращим вибором є використання AOP або саморобна інструменталізація.
jarnbjo

21
@jarnbjo: Ну, ви, звичайно, вітаєте вашу думку - але це досить добре налагоджена методика ІМО, і я, безумовно, використовував її з великим ефектом. Це не тільки покращує перевірку, але також робить вашу залежність від системного часу явною, що може бути корисно при отриманні огляду коду.
Джон Скіт

4
@ virgo47: Я думаю, що нам доведеться погодитися не погодитися. Я не вважаю, що "чітко заявляють ваші залежності" як забруднення коду - я вважаю це природним способом зробити код яснішим. Також я не бачу приховування цих залежностей за допомогою статичних дзвінків як "абсолютно прийнятних".
Джон Скіт

26
ОНОВЛЕННЯ Новий пакет java.time, вбудований в Java 8, включає java.time.Clockклас ", щоб дозволити підключенню альтернативних годин як і коли потрібно".
Василь Бурк

7
З цієї відповіді випливає, що в DI все добре. Особисто мені ще належить побачити додаток у реальному світі, який добре використовує його. Натомість ми бачимо велику кількість безглуздих окремих інтерфейсів, "об'єктів без стану", "об'єктів" лише для даних та класів низької згуртованості. ІМО, простота та об'єктно-орієнтований дизайн (з справжніми, державними об'єктами) - кращий вибір. Щодо staticметодів, переконайтеся, що вони не є ОО, але введення залежності без стану, екземпляр якої не працює в будь-якому інстанційному стані, насправді не кращий; це просто фантазійний спосіб маскування того, що в будь-якому разі є ефективно "статичним" поведінкою.
Rogério

78

тл; д-р

Чи є спосіб, або в коді, або з аргументами JVM, змінити поточний час, представлений через System.currentTimeMillis, окрім ручного зміни системного годинника на хост-машині?

Так.

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

Clock У java.time

Ми маємо нове рішення проблеми заміни підключеного годинника, щоб полегшити тестування з підробленими значеннями дати та часу. Пакет java.time в Java 8 включає абстрактний клас java.time.Clockіз чіткою метою:

щоб дозволити підключати альтернативні годинники як і коли потрібно

Ви можете підключити власну реалізацію Clock, хоча ви, ймовірно, можете знайти такий, який вже зроблений для задоволення ваших потреб. Для вашої зручності java.time включає статичні методи для отримання спеціальних реалізацій. Ці альтернативні реалізації можуть бути цінними під час тестування.

Змінена каденція

Різні tick…методи виробляють годинники, які збільшують поточний момент з різною каденцією.

За замовчуванням Clockповідомляється про час, оновлений так само часто, як мілісекунди в Java 8, а в Java 9 так само добре, як наносекунди (залежно від обладнання). Ви можете попросити повідомляти про справжній поточний момент з різною детальністю.

  • tickSeconds - Зростання за цілі секунди
  • tickMinutes - Збільшення за цілі хвилини
  • tick- Збільшення за переданим Durationаргументом.

Неправдиві годинники

Деякі годинники можуть лежати, створюючи результат, інший, ніж апаратний годинник хост-операційної системи.

  • fixed - Повідомляє про єдиний незмінний (не збільшується) момент як поточний момент.
  • offset- Звітує про поточний момент, але зміщений Durationаргументом, що передається .

Наприклад, замкніть у першу мить самого раннього Різдва цього року. Іншими словами, коли Санта та його оленя роблять свою першу зупинку . Найраніший часовий пояс в даний час , як видається, Pacific/Kiritimatiв +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 );

Використовуйте цей спеціальний фіксований годинник, щоб завжди повертати той самий момент. Ми отримуємо перший момент Різдва в Кірітіматі , з UTC, який показує годинник на чотирнадцять годин раніше, 10 ранку на попередню дату 24 грудня.

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

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

zdt.toString (): 2016-12-25T00: 00 + 14: 00 [Тихий океан / Кірітіматі]

Дивіться прямий код на IdeOne.com .

Справжній час, різний часовий пояс

Ви можете керувати, який часовий пояс призначений Clockреалізацією. Це може бути корисно в деяких тестуваннях. Але я не рекомендую це у виробничому коді, де завжди слід чітко вказувати необов'язкові ZoneIdчи ZoneOffsetаргументи.

Ви можете вказати, що UTC є зоною за замовчуванням.

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

Ви можете вказати будь-який конкретний часовий пояс. Вкажіть правильний час ім'я зони в форматі continent/region, наприклад America/Montreal, Africa/Casablancaабо Pacific/Auckland. Ніколи не використовуйте абревіатуру з 3–4 літер, наприклад, ESTабо ISTяк вони не є справжніми часовими поясами, не стандартизовані та навіть не унікальні (!).

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

Ви можете вказати, що поточний часовий пояс JVM за замовчуванням повинен бути типовим для певного Clockоб'єкта.

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

Запустіть цей код для порівняння. Зауважте, що всі вони повідомляють один і той же момент, ту саму точку на шкалі часу. Вони відрізняються лише настінним годинником ; іншими словами, три способи сказати одне і те ж, три способи відображення одного і того ж моменту.

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

America/Los_Angeles була поточною зоною за замовчуванням JVM на комп’ютері, на якому працює цей код.

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

zdtClockSystem.toString (): 2016-12-31T15: 52: 39.750-05: 00 [Америка / Монреаль]

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

InstantКлас завжди в UTC за визначенням. Отже, ці три зони, пов'язані із зоною, Clockмають абсолютно однаковий ефект.

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

Годинник за замовчуванням

Реалізація, яка використовується за замовчуванням для, Instant.now- це повернена Clock.systemUTC(). Це реалізація, яка використовується, коли ви не вказуєте a Clock. Переконайтесь у попередньому випуску вихідного коду Java 9 дляInstant.now .

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

Типовим Clockдля OffsetDateTime.nowі ZonedDateTime.nowє Clock.systemDefaultZone(). Див. Вихідний код .

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

Поведінка реалізацій за замовчуванням змінилася між Java 8 та Java 9. У Java 8 поточний момент фіксується з роздільною здатністю лише в мілісекундах, незважаючи на можливість класів зберігати роздільну здатність наносекунд . Java 9 пропонує нову реалізацію, здатну фіксувати поточний момент з роздільною здатністю наносекунд - залежно, звичайно, від можливостей годинника апаратного забезпечення вашого комп'ютера.


Про java.time

Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date, Calendar, і SimpleDateFormat.

Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - JSR 310 .

Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти до класів java.time .

Ви можете обмінюватися об'єктами java.time безпосередньо зі своєю базою даних. Використовуйте драйвер JDBC, сумісний з JDBC 4.2 або пізнішої версії. Немає потреби в струнах, немає потреби в java.sql.*заняттях. Hibernate 5 & JPA 2.2 підтримують java.time .

Де отримати класи java.time?


це правильна відповідь
бхарал

42

Як сказав Джон Скіт :

"використовувати Joda Time" - майже завжди найкраща відповідь на будь-яке запитання, що стосується "як мені домогтися X за допомогою java.util.Date/Calendar?"

Отже, ось (припускаючи, що ви щойно замінили все new Date()на 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();

Якщо ви хочете імпортувати бібліотеку, яка має інтерфейс (див. Коментар Йона нижче), ви можете просто скористатися годинником Prevayler , який забезпечить реалізацію, а також стандартний інтерфейс. Повна банка всього 96 кБ, тому вона не повинна зламати банк ...


13
Ні, я б не рекомендував цього - це не рухає вас до справді змінних годин; це дозволяє вам постійно використовувати статику. Використання часу Joda - це, звичайно, хороша ідея, але я все-таки хочу перейти з інтерфейсом Clock або подібним.
Джон Скіт

@Jon: Щоправда - для тестування це нормально, але краще мати інтерфейс.
Стефан

5
@Jon: Це ідеальний і простий спосіб IMHO, якщо ви хочете перевірити залежний від часу код, який вже використовує JodaTime. Спроба винайти колесо, ввівши ще один шар / рамку абстракції - це не шлях, якщо все вирішено для вас з JodaTime.
Стефан Хаберл

2
@JonSkeet: Про це Майк спочатку не просив. Він хотів інструмент для перевірки залежного від часу коду, а не повноцінний змінний годинник у виробничому коді. Я вважаю за краще зберігати свою архітектуру якомога складнішою, але максимально простою. Вводити тут додатковий шар абстракції (тобто інтерфейс) просто не потрібно
Стефан Хаберл

2
@StefanHaberl: Є багато речей, які не є строго "необхідними", але насправді, те, що я припускаю, робить вже наявну залежність явною і легше перевіряється в ізоляції. Підробка системного часу зі статикою має всі нормальні проблеми статики - це глобальний стан без поважних причин . Майк просив спосіб написати тестовий код, який використовував поточну дату та час. Я все ще вважаю, що моя пропозиція набагато чистіша (і робить залежність яснішою), ніж ефективно підробляти статику.
Джон Скіт

15

Хоча використання деякого шаблону DateFactory здається приємним, він не охоплює бібліотеки, якими ви не можете керувати - уявіть анотацію валідації @Past з реалізацією, що спирається на System.currentTimeMillis (є така).

Ось чому ми використовуємо jmockit для прямого висміювання системного часу:

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);

Оскільки неможливо дістатись до початкового розблокованого значення міліс, ми замість цього використовуємо нано-таймер - це не пов’язано з настінними годинниками, але тут достатньо часу:

// 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;
}

Існує документально підтверджена проблема, що час із HotSpot час повертається до норми після кількох дзвінків - ось звіт про проблему: http://code.google.com/p/jmockit/isissue/detail?id=43

Щоб подолати це, ми повинні ввімкнути одну конкретну оптимізацію HotSpot - запустити JVM з цим аргументом -XX:-Inline.

Хоча це може бути не ідеально підходить для виробництва, це просто чудово для тестів, і він абсолютно прозорий для застосування, особливо коли DataFactory не має сенсу для бізнесу і вводиться лише через тести. Було б непогано мати вбудований варіант JVM для запуску в різний час, занадто погано це неможливо без таких хаків.

Повна історія є у моєму дописі щоденника: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/

Повний зручний клас SystemTimeShifter надається у пості. Клас може бути використаний у ваших тестах, або він може бути використаний як перший основний клас перед вашим справжнім основним класом дуже легко, щоб запустити вашу програму (або навіть цілий додаток) в інший час. Звичайно, це спрямовано в основному на тестування, а не на виробниче середовище.

EDIT липня 2014 року: останнім часом JMockit сильно змінився, і ви повинні використовувати JMockit 1.0, щоб правильно використовувати це (IIRC). Однозначно не можна перейти на новітню версію, де інтерфейс абсолютно інший. Я замислювався над тим, щоб зв'язати лише необхідні речі, але оскільки нам це не потрібно в нових проектах, я взагалі не розвиваю цього.


1
Те, що ви змінюєте час для кожного класу, йде обома способами ... наприклад, якщо ви знущаєтеся з nanoTime замість currentTimeMillis, вам доведеться бути дуже обережними, щоб не зламати java.util.concurrent. Як правило, якщо бібліотеку сторонніх виробників важко використовувати в тестах, ви не повинні знущатися над входами бібліотеки: ви повинні знущатися над самою бібліотекою.
Dan Berindei

1
Ми використовуємо цю техніку для тестів, де ми не знущаємося над іншим. Це інтеграційний тест, і ми перевіряємо весь стек, чи він робить те, що повинен, коли якась активність трапляється вперше за рік і т.д. значення взагалі. Я хотів би бачити Java з функціями, що зміщують час, тому що мені вони вже не раз потрібні, і возитися з системним часом через це дуже не щастить.
virgo47

7

Powermock чудово працює. Просто використовував це для знущань System.currentTimeMillis().


Я спробував використати Powermock, і якби я це правильно зрозумів, він би насправді не знущався над стороною кале. Натомість він знаходить усіх абонентів, а потім застосовує макет. Це може зайняти дуже багато часу, оскільки ви повинні дозволити йому перевіряти КОЖЕН клас на своєму класі, якщо ви хочете бути справді впевненими, що ваша програма працює у змінений час. Виправте мене, якщо я помиляюся з приводу глузування Powermock.
virgo47

1
@ virgo47 Ні, ти це зрозумів неправильно. PowerMock ніколи не "перевірятиме кожен клас на вашому класі"; він буде перевіряти лише ті класи, які ви конкретно йому кажете перевірити (через @PrepareForTestанотацію до тестового класу).
Rogério

1
@ Rogério - саме так я це розумію - я сказав "ти мусиш це дозволити ...". Тобто, якщо ви очікуєте дзвінка в System.currentTimeMillisбудь-яку точку свого курсу (будь-яка версія), ви повинні перевірити кожен клас. Це я мав на увазі, нічого іншого. Справа в тому, що ви знущаєтесь над такою поведінкою на стороні абонента ("ви конкретно говорите"). Це нормально для простого тестування, але не для тестів, коли ви не можете бути впевнені, що викликає цей метод звідки (наприклад, більш складні тести компонентів із залученими бібліотеками). Це не означає, що Powermock зовсім не так, це просто означає, що ви не можете використовувати його для такого типу тесту.
virgo47

1
@ virgo47 Так, я бачу, що ти маєш на увазі. Я думав, ти маєш на увазі, що PowerMock повинен буде автоматично перевірити кожен клас на classpath, що, очевидно, було б абсурдом. На практиці, однак, для одиничних тестів ми зазвичай знаємо, який клас (и) читає час, тому його не задавати; для інтеграційних тестів, дійсно, вимога в PowerMock, щоб усі вказані класи були вказані, може бути проблемою.
Rogério

6

Використовуйте програмування, орієнтоване на аспекти (AOP, наприклад, AspectJ) для переплетення системного класу, щоб повернути заздалегідь задане значення, яке ви можете встановити у своїх тестових випадках.

Або переплетення класів додатки для перенаправлення виклику System.currentTimeMillis()або new Date()іншого клас утиліти самостійно.

Класи систем ткацтва (java.lang.* ), однак, трохи складніше, і вам може знадобитися виконати офлайн-плетіння для rt.jar та використовувати окремий JDK / rt.jar для своїх тестів.

Це називається Бінарне плетіння, а також існують спеціальні інструменти для виконання плетіння системних класів та обходу деяких проблем із цим (наприклад, завантажувальна програма VM може не працювати)


Це, звичайно, працює, але це набагато простіше зробити з глузливим інструментом, ніж з інструментом AOP; останній вид інструменту є надто загальним для цілей, про які йдеться.
Rogério

3

Насправді не існує способу зробити це безпосередньо у віртуальному комп'ютері, але ви можете все-таки програмно встановити системний час на тестовій машині. У більшості (усіх?) ОС для цього є команди командного рядка.


Наприклад, на windows dateі timeкоманди. У Linux dateкоманда.
Джеремі Реймонд

Чому це не можна зробити за допомогою АОП або інструменталізації (це було зроблено)?
jarnbjo

3

Працюючий спосіб змінити поточний системний час для цілей тестування JUnit у веб-додатку Java 8 за допомогою EasyMock, без Joda Time та без PowerMock.

Ось що потрібно зробити:

Що потрібно зробити в тестованому класі

Крок 1

Додайте новий java.time.Clockатрибут до тестованого класу MyServiceта переконайтесь, що новий атрибут буде ініціалізований належним чином за значеннями за замовчуванням із блоком екземплярів чи конструктором:

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
  }
  // (...)
}

Крок 2

Введіть новий атрибут clockу метод, який вимагає поточного часу. Наприклад, у моєму випадку мені довелося перевірити, чи не відбулася раніше дата, збережена в базі даних LocalDateTime.now(), яку я замінив 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();
    }
  }
  // (...) 
}

Що потрібно зробити в тестовому класі

Крок 3

У тестовому класі створіть об'єкт макетного годинника та введіть його в екземпляр тестованого класу безпосередньо перед тим, як викликати тестований метод doExecute(), а потім відновіть його назад одразу після цього, наприклад:

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(..)
    }
  }

Перевірте це в режимі налагодження, і ви побачите, що дата 2017 року 3 лютого була правильно введена в myServiceекземпляр і використана в інструкції щодо порівняння, а потім була належним чином скинута до поточної дати initDefaultClock().


2

На мою думку, тільки неінвазивне рішення може працювати. Особливо, якщо у вас є зовнішні лібри та велика база застарілого коду, немає надійного способу знущатися над часом.

JMockit ... працює лише для обмеженої кількості разів

PowerMock & Co ... потрібно знущатися з клієнтів до System.currentTimeMillis (). Знову інвазивний варіант.

З цього я бачу тільки згаданий javaagent або АОП підходу є прозорим для всієї системи. Хтось це робив і чи міг би вказати на таке рішення?

@jarnbjo: чи не могли б ви показати якийсь код Java-агента?


3
Рішення jmockit працює необмежену кількість разів з невеликим -XX: -Вбудований хак (на HotSpot Sun): virgo47.wordpress.com/2012/06/22/changing-system-time-in-java Повний зручний клас SystemTimeShifter надається в дописі. Клас може бути використаний у ваших тестах, або він може бути використаний як перший основний клас перед вашим справжнім основним класом дуже легко, щоб запустити вашу програму (або навіть цілий додаток) в інший час. Звичайно, це спрямовано в основному на тестування, а не на виробниче середовище.
virgo47

2

Якщо ви працюєте з Linux, ви можете використовувати головну гілку libfaketime або під час тестування виконувати 4ce2835 .

Просто встановіть змінну середовища з часом, з якого ви хочете знущатися над додатком java, і запускайте його за допомогою ld-попередньої завантаження:

# 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

Друга змінна середовище є першорядною для програм Java, які в іншому випадку замерзають. Це вимагає головного відділення libfaketime на момент написання.

Якщо ви хочете змінити час системної керованої служби, просто додайте до переліку змін свого файлу одиниці, наприклад, для гучного пошуку це буде /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"

Не забудьте перезавантажити systemd за допомогою `systemctl daemon-reload


-1

Якщо ви хочете знущатися над методом, що має System.currentTimeMillis()аргумент, тоді ви можете передатиanyLong() клас Matchers як аргумент.

PS Я можу успішно запустити свій тестовий випадок, використовуючи наведений вище трюк, і просто поділитися деталями щодо мого тесту про те, що я використовую рамки PowerMock та Mockito.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.