समय संवेदनशील कोड के परीक्षण के लिए Java System.currentTimeMillis को ओवरराइड करें


129

वहाँ एक तरीका है, या तो कोड में या JVM तर्कों के साथ, वर्तमान समय को ओवरराइड करने के लिए, जैसा कि प्रस्तुत किया System.currentTimeMillisगया है, मेजबान मशीन पर सिस्टम घड़ी को मैन्युअल रूप से बदलने के अलावा अन्य है?

थोड़ा पृष्ठभूमि:

हमारे पास एक ऐसी प्रणाली है जो कई लेखांकन कार्य चलाती है जो कि वर्तमान तिथि के आसपास उनके तर्क का बहुत घूमती है (अर्थात महीने का पहला, वर्ष का पहला, आदि)

दुर्भाग्य से, बहुत सी विरासत कोड कॉल फ़ंक्शन जैसे new Date()या Calendar.getInstance(), दोनों अंततः कॉल डाउन करते हैं System.currentTimeMillis

परीक्षण उद्देश्यों के लिए, अभी, हम सिस्टम घड़ी को मैन्युअल रूप से अपडेट करने के लिए अटक गए हैं कि किस समय और दिनांक को लगता है कि परीक्षण चलाया जा रहा है।

तो मेरा सवाल है:

वहाँ एक तरीका है जो द्वारा वापस आ गया है को ओवरराइड करने के लिए है System.currentTimeMillis? उदाहरण के लिए, JVM को स्वचालित रूप से जोड़ने या उस विधि से लौटने से पहले कुछ ऑफसेट को घटाना बताने के लिए?

अग्रिम में धन्यवाद!


1
मुझे नहीं पता कि यह अब और प्रासंगिक है, लेकिन AspectJ के साथ इसे हासिल करने के लिए एक और तरीका है, मेरा जवाब यहां देखें: stackoverflow.com/questions/18239859/…
Nándor Előd Fekete

@ NándorElődFekete लिंक में समाधान दिलचस्प है, हालांकि इसके लिए कोड को फिर से जमा करना आवश्यक है। मुझे आश्चर्य है कि अगर मूल पोस्टर में फिर से इकट्ठा करने की क्षमता है, तो इस तथ्य को देखते हुए कि वह विरासत कोड से निपटने का दावा कर रहा है।
क्लेबरज

1
@cleberz AspectJ के अच्छे गुणों में से एक यह है कि यह सीधे बायटेकोड पर काम करता है, इसलिए इसे काम करने के लिए मूल स्रोत कोड की आवश्यकता नहीं है।
नंदोर एल्द फ़ेकेट

@ NándorElődFekete संकेत के लिए बहुत बहुत धन्यवाद। मैं जेएसटेक-स्तरीय इंस्ट्रूमेंटेशन (विशेषकर जेडीके क्लासेस इंस्ट्रूमेंटेशन) से अनजान था। मुझे कुछ समय लगा लेकिन मुझे यह पता लगाने में सक्षम था कि मुझे अपनी जरूरतों को पूरा करने के लिए rt.jar के साथ-साथ गैर-jdk कक्षाओं की एक लोड-टाइम बुनाई के साथ-साथ एक ओवर-टाइम सिस्टम भी करना था। currentTimeMillis () और System.nanoTime ())।
क्लेबर्ज़

@ अंकज मेरे पास जेआरई कक्षाओं के माध्यम से बुनाई का एक और उत्तर है , आप इसे भी देख सकते हैं।
नंदोर एल्द फेकेट

जवाबों:


115

मैं दृढ़ता से सलाह देता हूं कि सिस्टम घड़ी के साथ गड़बड़ करने के बजाय, आप एक बदली हुई घड़ी का उपयोग करने के लिए उस विरासत कोड को बुलेट और रिफैक्टर को काटते हैं। आदर्श रूप से यह निर्भरता इंजेक्शन के साथ किया जाना चाहिए, लेकिन यहां तक ​​कि अगर आप एक बदली सिंगलटन का उपयोग करते हैं तो आप परीक्षण क्षमता हासिल करेंगे।

इसे लगभग सिंगलटन संस्करण के लिए खोज और प्रतिस्थापित किया जा सकता है:

  • बदलें Calendar.getInstance()के साथ Clock.getInstance().getCalendarInstance()
  • बदलें new Date()के साथClock.getInstance().newDate()
  • बदलें System.currentTimeMillis()के साथClock.getInstance().currentTimeMillis()

(आवश्यकता के अनुसार)

एक बार जब आप पहला कदम उठा लेते हैं, तो आप एक समय में सिंगलटन को DI से थोड़ा बदल सकते हैं।


50
परीक्षण क्षमता बढ़ाने के लिए सभी संभावित एपीआई को अमूर्त या लपेटकर अपने कोड को अव्यवस्थित करना IMHO एक बहुत अच्छा विचार नहीं है। यहां तक ​​कि अगर आप एक बार एक सरल खोज और प्रतिस्थापन के साथ रीफैक्टरिंग कर सकते हैं, तो कोड को पढ़ना, समझना और बनाए रखना बहुत कठिन हो जाता है। कई नकली चौखटे के लिए System.currentTimeMillis के व्यवहार को संशोधित करने में सक्षम होना चाहिए, यदि नहीं, तो AOP या स्व-निर्मित इंस्ट्रुमेंटलाइज़ेशन का उपयोग करना बेहतर विकल्प है।
जर्बंजो

21
@ जर्नबजो: ठीक है, आपकी राय में आपका स्वागत है - लेकिन यह एक बहुत अच्छी तरह से स्थापित तकनीक आईएमओ है, और मैंने निश्चित रूप से इसे महान प्रभाव के लिए इस्तेमाल किया है। न केवल यह परीक्षण क्षमता में सुधार करता है - यह सिस्टम समय पर आपकी निर्भरता को भी स्पष्ट करता है, जो कोड का अवलोकन करते समय उपयोगी हो सकता है।
जॉन स्कीट

4
@ virgo47: मुझे लगता है कि हम असहमत होने के लिए सहमत होंगे। मैं कोड को प्रदूषित करने के रूप में "अपनी निर्भरता स्पष्ट रूप से बताते हुए" नहीं देखता - मैं इसे कोड को स्पष्ट करने के प्राकृतिक तरीके के रूप में देखता हूं। न ही मैं उन आश्रितों को स्थैतिक कॉल के माध्यम से छुपाने के लिए अनावश्यक रूप से "पूरी तरह से स्वीकार्य" के रूप में देखता हूं।
जॉन स्कीट

26
अद्यतन जावा 8 में निर्मित नए जावा.टाइम पैकेज में java.time.Clock"आवश्यक होने पर वैकल्पिक घड़ियों को प्लग करने की अनुमति देने के लिए" एक वर्ग शामिल है ।
बेसिल बॉर्क

7
यह उत्तर बताता है कि DI सब अच्छा है। निजी तौर पर, मुझे वास्तविक दुनिया में एक आवेदन देखना बाकी है जो इसका अच्छा उपयोग करता है। इसके बजाय, हम निरर्थक अलग इंटरफेस, स्टेटलेस "ऑब्जेक्ट्स", डेटा-ओनली "ऑब्जेक्ट्स", और लो कोइशेस क्लासेस की एक गहराई देखते हैं। IMO, सादगी और ऑब्जेक्ट-ओरिएंटेड डिज़ाइन (सच, स्टेटफुल ऑब्जेक्ट्स के साथ) एक बेहतर विकल्प है। staticविधियों के बारे में , सुनिश्चित करें कि वे OO नहीं हैं, लेकिन एक स्टेटलेस निर्भरता को इंजेक्ट कर रहे हैं, जिसका उदाहरण किसी भी उदाहरण स्थिति पर काम नहीं करता है, वास्तव में बेहतर नहीं है; यह वैसे ही प्रभावी ढंग से "स्थिर" व्यवहार करने के लिए भ्रामक तरीका है।
रोजेरियो

78

tl; डॉ

वहाँ एक तरीका है, या तो कोड में या JVM तर्कों के साथ, वर्तमान समय को ओवरराइड करने के लिए, जैसा कि System.currentTimeMillis के माध्यम से प्रस्तुत किया गया है, मेजबान मशीन पर सिस्टम घड़ी को मैन्युअल रूप से बदलने के अलावा?

हाँ।

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

Clock Java.time में

हमारे पास फॉक्स डेट-टाइम मान के साथ परीक्षण की सुविधा के लिए एक प्लग करने योग्य घड़ी प्रतिस्थापन की समस्या का एक नया समाधान है । Java.time पैकेज में जावा 8 एक अमूर्त वर्ग भी शामिल है java.time.Clock, एक स्पष्ट उद्देश्य के साथ:

आवश्यकता पड़ने पर वैकल्पिक घड़ियों को प्लग करने की अनुमति दें

आप अपने स्वयं के कार्यान्वयन में प्लग Clockकर सकते हैं, हालांकि आप अपनी आवश्यकताओं को पूरा करने के लिए पहले से ही बना हुआ पा सकते हैं। आपकी सुविधा के लिए, java.time में विशेष कार्यान्वयन प्राप्त करने के लिए स्थिर तरीके शामिल हैं। ये वैकल्पिक कार्यान्वयन परीक्षण के दौरान मूल्यवान हो सकते हैं।

बदल गया ताल

विभिन्न tick…विधियाँ घड़ियों का निर्माण करती हैं जो एक अलग ताल के साथ वर्तमान क्षण को बढ़ाती हैं।

डिफ़ॉल्ट Clockसमय जावा 8 में मिलीसेकंड और जावा 9 में नैनोसेकंड (अपने हार्डवेयर पर निर्भर करता है) के रूप में अक्सर अपडेट किए गए समय की रिपोर्ट करता है। आप सही वर्तमान क्षण के लिए एक अलग ग्रैन्युलैरिटी के साथ रिपोर्ट करने के लिए कह सकते हैं।

  • tickSeconds - पूरे सेकंड में वृद्धि
  • tickMinutes - पूरे मिनट में वृद्धि
  • tick- पारित Durationतर्क द्वारा वृद्धि ।

झूठी घड़ियाँ

कुछ घड़ियाँ झूठ बोल सकती हैं, जो मेजबान OS की हार्डवेयर घड़ी की तुलना में भिन्न परिणाम देती हैं।

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

हमेशा उसी पल को लौटाने के लिए उस विशेष निश्चित घड़ी का उपयोग करें। हमें किरीटीमाटी में क्रिसमस के दिन का पहला क्षण मिलता है , यूटीसी के साथ चौदह घंटे पहले की दीवार-घड़ी का समय दिखाते हुए, 24 दिसंबर की पूर्व तारीख को सुबह 10 बजे।

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

तत्काल .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" ) ) );

आप निर्दिष्ट कर सकते हैं जेवीएम का वर्तमान डिफ़ॉल्ट समय क्षेत्र किसी विशेष 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 [अमेरिका / Los_Angeles]

Instantवर्ग परिभाषा के द्वारा यूटीसी में हमेशा होता है। इसलिए इन तीनों ज़ोन से जुड़े 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()। जब आप निर्दिष्ट नहीं करते हैं तो यह कार्यान्वयन होता है Clock। स्वयं देखें में के लिए रिलीज जावा 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());
}

जावा 8 और जावा 9 के बीच डिफ़ॉल्ट कार्यान्वयन का व्यवहार बदल गया है। जावा 8 में, वर्तमान क्षण को नैनोसेकंड के रिज़ॉल्यूशन को संग्रहीत करने की कक्षाओं की क्षमता के बावजूद केवल मिलीसेकंड में रिज़ॉल्यूशन के साथ कैप्चर किया गया है । जावा 9 एक नया कार्यान्वयन लाता है जो वर्तमान क्षण को नैनोसेकंड के एक संकल्प के साथ कैप्चर करने में सक्षम है - यह आपके कंप्यूटर हार्डवेयर घड़ी की क्षमता पर निर्भर करता है।


के बारे में जावा के

Java.time ढांचे जावा 8 और बाद में बनाया गया है। इन कक्षाओं परेशानी वर्ष प्रतिस्थापित विरासत जैसे तिथि-समय कक्षाएं java.util.Date, Calendar, औरSimpleDateFormat

अधिक जानने के लिए, Oracle ट्यूटोरियल देखें । और कई उदाहरणों और स्पष्टीकरणों के लिए ढेर अतिप्रवाह खोजें। विशिष्टता JSR 310 है

Joda समय परियोजना, अब में रखरखाव मोड , प्रवास करने की सलाह देता java.time कक्षाएं।

आप अपने डेटाबेस से सीधे java.time वस्तुओं का आदान-प्रदान कर सकते हैं । JDBC 4.2 या उसके बाद के JDBC ड्राइवर का अनुपालन करें । तारों की कोई जरूरत नहीं, कक्षाओं की कोई जरूरत नहीं । हाइबरनेट 5 और जेपीए 2.2 जावा का समर्थन करते हैंjava.sql.*

Java.time कक्षाएं कहाँ से प्राप्त करें?


यह सही जवाब है
Bharal

42

के रूप में जॉन स्कीट से कहा :

"Joda Time का उपयोग करें" लगभग हमेशा किसी भी सवाल का सबसे अच्छा जवाब होता है "मैं java.util.Date/Calendar के साथ X कैसे प्राप्त करूं?"

तो यहाँ जाता है (यह मानते हुए आप बस सब बदल दिया है आपके 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();

यदि आप एक ऐसे पुस्तकालय का आयात करना चाहते हैं जिसका इंटरफ़ेस है (नीचे जॉन की टिप्पणी देखें), तो आप सिर्फ प्रीवलेर क्लॉक का उपयोग कर सकते हैं , जो कार्यान्वयन के साथ-साथ मानक इंटरफ़ेस भी प्रदान करेगा। पूर्ण जार केवल 96kB है, इसलिए इसे बैंक को नहीं तोड़ना चाहिए ...


13
नहीं, मैं इसकी सिफारिश नहीं करूंगा - यह आपको वास्तव में बदली जाने वाली घड़ी की ओर नहीं ले जाता है; यह आपको पूरे स्टैटिक्स का उपयोग करता है। Joda Time का उपयोग करना निश्चित रूप से एक अच्छा विचार है, लेकिन मैं अभी भी क्लॉक इंटरफ़ेस या इसी तरह के साथ जाना चाहूंगा।
जॉन स्कीट

@ जॉन: ट्रू - टेस्टिंग के लिए, यह ठीक है, लेकिन एक इंटरफ़ेस रखना बेहतर है।
स्टीफन

5
@Jon: यदि आप समय पर निर्भर कोड का परीक्षण करना चाहते हैं तो यह एक सुगम और IMHO सही और आसान तरीका है जो पहले से ही JodaTime का उपयोग करता है। अभी तक एक और अमूर्त परत / रूपरेखा पेश करके पहिया को मजबूत करने की कोशिश कर रहा है, अगर पहले से ही JodaTime के साथ आपके लिए सब कुछ हल नहीं हुआ था।
स्टीफन हैबरल

2
@JonSkeet: यह वह नहीं है जो माइक मूल रूप से पूछ रहा था। वह समय पर निर्भर कोड का परीक्षण करने के लिए एक उपकरण चाहते थे और उत्पादन कोड में एक पूर्ण विकसित बदली घड़ी नहीं। मैं अपनी वास्तुकला को यथासंभव जटिल रखना पसंद करता हूं लेकिन जितना संभव हो उतना सरल। यहाँ अमूर्त (अर्थात इंटरफ़ेस) की एक अतिरिक्त परत प्रस्तुत करना आवश्यक नहीं है
स्टीफन हैबर

2
@StefanHaberl: ऐसी कई चीजें हैं जो कड़ाई से "आवश्यक" नहीं हैं - लेकिन वास्तव में मैं जो सुझाव दे रहा हूं वह पहले से मौजूद निर्भरता को स्पष्ट कर रहा है और अलगाव में आसानी से परीक्षण योग्य है। स्टैटिक्स के साथ सिस्टम के समय को रोकना स्टैटिक्स की सभी सामान्य समस्याएं हैं - यह बिना किसी अच्छे कारण के वैश्विक स्थिति है । माइक परीक्षण योग्य कोड लिखने का तरीका पूछ रहा था जिसमें वर्तमान तिथि और समय का उपयोग किया गया था। मेरा मानना ​​है कि मेरा सुझाव अभी तक क्लीनर (और निर्भरता को स्पष्ट करता है) की तुलना में प्रभावी रूप से एक स्थैतिक ठगना है।
जॉन स्कीट

15

कुछ DateFactory पैटर्न का उपयोग करते हुए अच्छा लगता है, यह उन पुस्तकालयों को कवर नहीं करता है जिन्हें आप नियंत्रित नहीं कर सकते हैं - System.currentTimeMillis पर निर्भर कार्यान्वयन के साथ सत्यापन एनोटेशन @Past की कल्पना करें (वहाँ ऐसा है)।

इसीलिए हम सिस्टम टाइम को सीधे मॉक करने के लिए जॉकिट का उपयोग करते हैं:

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

प्रलेखित समस्या है, कि हॉटस्पॉट के साथ कई कॉल के बाद समय सामान्य हो जाता है - यहाँ समस्या रिपोर्ट है: http://code.google.com/p/jmockit/issues/detail?id=43

इसे दूर करने के लिए हमें एक विशिष्ट हॉटस्पॉट अनुकूलन चालू करना होगा - इस तर्क के साथ जेवीएम चलाएं -XX:-Inline

हालांकि यह उत्पादन के लिए बिल्कुल सही नहीं हो सकता है, यह परीक्षणों के लिए ठीक है और यह आवेदन के लिए बिल्कुल पारदर्शी है, खासकर जब डेटाफ़ैक्ट व्यवसाय की समझ में नहीं आता है और केवल परीक्षणों के कारण पेश किया जाता है। अलग-अलग समय में चलने के लिए बिल्ट-इन जेवीएम विकल्प का होना अच्छा होगा, बहुत बुरा ऐसा बिना हैक के संभव नहीं है।

पूरी कहानी मेरे ब्लॉग पोस्ट में यहाँ है: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/

पोस्ट में पूरी तरह से उपयोगी क्लास सिस्टम टाइमशैफ्टर दिया गया है। आपके परीक्षणों में कक्षा का उपयोग किया जा सकता है, या इसे आपके मुख्य मुख्य वर्ग के पहले मुख्य वर्ग के रूप में इस्तेमाल किया जा सकता है ताकि एक अलग समय में आपके आवेदन (या यहां तक ​​कि संपूर्ण परिचर) को चलाने के लिए बहुत आसानी से किया जा सके। बेशक, यह मुख्य रूप से उत्पादन के लिए नहीं, परीक्षण उद्देश्यों के लिए इरादा है।

EDIT जुलाई 2014: JMockit ने हाल ही में बहुत कुछ बदल दिया और आप JMockit 1.0 को सही तरीके से उपयोग करने के लिए बाध्य हैं (IIRC)। निश्चित रूप से नवीनतम संस्करण में अपग्रेड नहीं किया जा सकता है जहां इंटरफ़ेस पूरी तरह से अलग है। मैं केवल आवश्यक सामानों के बारे में सोच रहा था, लेकिन जैसा कि हमें अपनी नई परियोजनाओं में इस चीज की आवश्यकता नहीं है, मैं इस चीज को विकसित नहीं कर रहा हूं।


1
यह तथ्य कि आप हर वर्ग के लिए समय बदल रहे हैं, दोनों तरह से जाता है ... उदाहरण के लिए, यदि आप वर्तमान समय के बजाय नैनोमॉक का मजाक उड़ाएंगे तो आपको सावधान रहना होगा कि आप java.util.concurrent को न तोड़ें। अंगूठे के एक नियम के रूप में, यदि एक 3 पार्टी पुस्तकालय परीक्षणों में उपयोग करना कठिन है, तो आपको पुस्तकालय के इनपुट का मजाक नहीं उड़ाना चाहिए: आपको पुस्तकालय का मजाक उड़ाना चाहिए।
दान बेरींडी

1
हम इस तकनीक का उपयोग उन परीक्षणों के लिए करते हैं जहां हम कुछ और नकली नहीं करते हैं। यह एकीकरण परीक्षण है और हम पूरे स्टैक का परीक्षण करते हैं कि क्या यह ऐसा करता है जब वर्ष में पहली बार कुछ गतिविधि होती है, आदि। नैनो टाइम के बारे में अच्छी बात, सौभाग्य से हमें यह नकली करने की आवश्यकता नहीं है क्योंकि इसमें कोई दीवार-घड़ी नहीं है मतलब बिल्कुल। मैं समय के साथ सुविधाओं को स्थानांतरित करने के साथ जावा को देखना पसंद करूंगा क्योंकि मुझे उन्हें एक से अधिक बार पहले से ही जरूरत थी और इस वजह से सिस्टम समय के साथ खिलवाड़ करना बहुत अशुभ है।
virgo47

7

पावरमॉक शानदार काम करता है। बस इसका इस्तेमाल मजाक करने के लिए कियाSystem.currentTimeMillis()


मैंने पावरमॉक का उपयोग करने की कोशिश की, और अगर मैंने इसे सही ढंग से समझा तो यह वास्तव में कैली की ओर से नकली नहीं होगा। इसके बजाय यह सभी कॉलर्स को ढूंढता है और मॉक लागू करता है। यदि आपके पास शिफ्ट किए गए समय में आपके आवेदन का काम वास्तव में होना है तो यह सुनिश्चित करने में आपको बहुत समय लग सकता है क्योंकि आपको इसे अपने वर्गपथ में हर वर्ग की जाँच करने देना होगा। मुझे सही करें अगर मैं पॉमरॉक मॉकिंग के तरीके के बारे में गलत हूं।
virgo47

1
@ virgo47 नहीं, आपने इसे गलत समझा। PowerMock कभी भी "आपके वर्गपथ के हर वर्ग की जाँच नहीं करेगा"; यह केवल उन कक्षाओं की जांच करेगा जो आप विशेष रूप से इसे जांचने के लिए कहते हैं ( @PrepareForTestपरीक्षण वर्ग पर एनोटेशन के माध्यम से )।
रोजेरियो

1
@ रोजेरियो - ठीक इसी तरह से मैं इसे समझता हूँ - मैंने कहा "आपको इसे करने देना होगा ..."। यदि आप System.currentTimeMillisअपने वर्गपथ (किसी भी परिवाद) पर कहीं भी कॉल करने की अपेक्षा करते हैं, तो आपको हर वर्ग की जांच करनी होगी। यही मेरा मतलब था, और कुछ नहीं। मुद्दा यह है कि आप कॉलर के पक्ष में उस व्यवहार का मजाक उड़ाते हैं ("आप विशेष रूप से बताते हैं")। यह सरल परीक्षण के लिए ठीक है, लेकिन उन परीक्षणों के लिए नहीं जहां आप यह सुनिश्चित नहीं कर सकते कि उस विधि को कहां से कॉल करें (उदाहरण के लिए पुस्तकालयों के साथ अधिक जटिल घटक परीक्षण)। इसका मतलब यह नहीं है कि पॉवर्म बिल्कुल गलत है, इसका मतलब यह है कि आप इस प्रकार के परीक्षण के लिए इसका उपयोग नहीं कर सकते।
virgo47

1
@ virgo47 हां, मैं देख रहा हूं कि आपका क्या मतलब है। मुझे लगा कि आपका मतलब है कि पावरमॉक को क्लासपैथ में हर वर्ग की स्वचालित रूप से जांच करनी होगी, जो स्पष्ट रूप से बेतुका होगा। व्यवहार में, हालांकि, यूनिट परीक्षणों के लिए हम आमतौर पर जानते हैं कि कौन सा वर्ग (एस) समय पढ़ता है, इसलिए इसे निर्दिष्ट करने का मुद्दा नहीं है; एकीकरण परीक्षणों के लिए, वास्तव में, निर्दिष्ट सभी वर्गों का उपयोग करने के लिए पावरमॉक में आवश्यकता एक समस्या हो सकती है।
रोजरियो

6

पूर्वनिर्धारित मान जो आप अपने परीक्षण मामलों में निर्धारित कर सकते हैं वापस करने के लिए सिस्टम वर्ग की बुनाई के लिए एस्पेक्ट-ओरिएंटेड प्रोग्रामिंग (AOP, उदाहरण के लिए AspectJ) का उपयोग करें।

या आवेदन कक्षाओं बुनाई करने के लिए कॉल पुनर्निर्देशित करने के लिए System.currentTimeMillis()या करने के लिए new Date()अपना खुद का एक और उपयोगिता वर्ग के लिए।

बुनाई प्रणाली कक्षाएं (java.lang.* ) हालांकि थोड़ा अधिक पेचीदा है और आपको rt.jar के लिए ऑफ़लाइन बुनाई करने और अपने परीक्षणों के लिए एक अलग JDK / rt.jar का उपयोग करने की आवश्यकता हो सकती है।

इसे द्विआधारी बुनाई कहा जाता है और सिस्टम कक्षाओं की बुनाई करने के लिए और इसके साथ कुछ समस्याओं को दरकिनार करने के लिए विशेष उपकरण भी हैं (उदाहरण के लिए वीएम बूटस्ट्रैपिंग काम नहीं कर सकती है)


यह निश्चित रूप से काम करता है, लेकिन AOP टूल की तुलना में मॉकिंग टूल के साथ करना बहुत आसान है; उत्तरार्द्ध प्रकार का उपकरण प्रश्न में उद्देश्य के लिए सिर्फ सामान्य है।
रोजरियो

3

वीएम में सीधे ऐसा करने का कोई तरीका नहीं है, लेकिन आप प्रोग्राम मशीन पर सिस्टम टाइम को प्रोग्राम करने के लिए कुछ कर सकते हैं। अधिकांश (सभी?) OS के पास ऐसा करने के लिए कमांड लाइन कमांड हैं।


उदाहरण के लिए, विंडोज़ dateऔर timeकमांड पर। लिनक्स पर dateकमांड।
जेरेमी रेमंड

एओपी या इंस्ट्रूमेंटलाइजेशन के साथ ऐसा करना संभव क्यों नहीं होना चाहिए (क्या वहां ऐसा किया गया है)?
जर्बंजो

3

एक जावा 8 वेब अनुप्रयोग में ईयूएमकॉक के साथ, जोडा टाइम के बिना, और पावरमॉक के बिना जेयूनाइट परीक्षण उद्देश्यों के लिए वर्तमान सिस्टम समय को ओवरराइड करने का एक तरीका है।

यहाँ आपको क्या करना है:

परीक्षित वर्ग में क्या करने की आवश्यकता है

चरण 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 () को क्लाइंट्स को मॉक करना होगा । फिर से एक आक्रामक विकल्प।

इसमें से मैं केवल उल्लेखित jagagent या aop अप्रोच को पूरे सिस्टम के लिए पारदर्शी देखता हूँ । क्या किसी ने ऐसा किया है और ऐसे समाधान की ओर इशारा कर सकता है?

@ जर्नजो: क्या आप कुछ जैवागेंट कोड दिखा सकते हैं?


3
jmockit समाधान एक छोटी -XX के साथ समय की असीमित संख्या के लिए काम कर रहा है: -इनलाइन हैक (सूर्य के हॉटस्पॉट पर): virgo47.wordpress.com/2012/06/22/changing-system-time-in-java पूरा काम सिस्टम टाइमशैफ्टर प्रदान किया गया है पोस्ट में। आपके परीक्षणों में कक्षा का उपयोग किया जा सकता है, या इसे आपके मुख्य मुख्य वर्ग के पहले मुख्य वर्ग के रूप में इस्तेमाल किया जा सकता है ताकि एक अलग समय में आपके एप्लिकेशन (या पूरे संपूर्ण पर्यवेक्षक) को चलाने के लिए बहुत आसानी से किया जा सके। बेशक, यह मुख्य रूप से उत्पादन के लिए नहीं, परीक्षण उद्देश्यों के लिए इरादा है।
virgo47

2

यदि आप लिनक्स चला रहे हैं, तो आप libfaketime की मास्टर शाखा का उपयोग कर सकते हैं, या परीक्षण के समय 4ce2835 कर सकते हैं

बस उस समय के साथ पर्यावरण चर सेट करें, जब आप अपने जावा एप्लिकेशन का मजाक उड़ाते हैं, और ld-preloading का उपयोग करके इसे चलाते हैं:

# 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

दूसरा पर्यावरण चर जावा अनुप्रयोगों के लिए सर्वोपरि है, जो अन्यथा जम जाएगा। इसे लिखने के समय लिबफाकटाइम की मास्टर शाखा की आवश्यकता होती है।

यदि आप एक सिस्टम प्रबंधित सेवा के समय को बदलना चाहते हैं, तो बस अपनी यूनिट फ़ाइल ओवरराइड्स में निम्न जोड़ें, जैसे कि इलास्टिक्सर्च के लिए यह होगा /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"

`Systemctl डेमॉन-रीलोड का उपयोग कर सिस्टम को पुनः लोड करना न भूलें


-1

यदि आप System.currentTimeMillis()तर्क anyLong()युक्त विधि का मजाक उड़ाना चाहते हैं तो आप तर्क के रूप में माचिस की श्रेणी से गुजर सकते हैं ।

PS मैं उपरोक्त परीक्षण का उपयोग करके अपने परीक्षण के मामले को सफलतापूर्वक चलाने में सक्षम हूं और अपने परीक्षण के बारे में अधिक जानकारी साझा करने के लिए कि मैं पॉवरमॉक और मॉकिटो फ्रेमवर्क का उपयोग कर रहा हूं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.