Joda Time के लिए एक अच्छा DateTimeUtils.setCurrentMillisFixed () मॉक टाइम है।
यह परीक्षणों में बहुत व्यावहारिक है।
क्या जावा 8 के जावा.टाइम एपीआई में एक समान है ?
Joda Time के लिए एक अच्छा DateTimeUtils.setCurrentMillisFixed () मॉक टाइम है।
यह परीक्षणों में बहुत व्यावहारिक है।
क्या जावा 8 के जावा.टाइम एपीआई में एक समान है ?
जवाबों:
निकटतम Clock
वस्तु वस्तु है। आप किसी भी समय (या सिस्टम वर्तमान समय से) का उपयोग करके एक घड़ी ऑब्जेक्ट बना सकते हैं। सभी date.time ऑब्जेक्ट्स में ओवरलोडेड now
विधियाँ हैं जो वर्तमान समय के बजाय घड़ी ऑब्जेक्ट लेती हैं। तो आप एक विशिष्ट समय के साथ घड़ी को इंजेक्ट करने के लिए निर्भरता इंजेक्शन का उपयोग कर सकते हैं:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
देखें घड़ी JavaDoc अधिक जानकारी के लिए
मैंने Clock.fixed
सृजन को छिपाने और परीक्षणों को सरल बनाने के लिए एक नए वर्ग का उपयोग किया :
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()
विधि को हटा सकते हैं और सीधे क्षेत्र का उपयोग कर सकते हैं। इस पद्धति में कोड की कुछ पंक्तियों के अलावा कुछ नहीं है।
मैंने एक मैदान का इस्तेमाल किया
private Clock clock;
और फिर
LocalDate.now(clock);
मेरे उत्पादन कोड में। तब मैंने अपने यूनिट परीक्षणों में मॉकिटो का उपयोग क्लॉक का उपयोग करके किया था।
@Mock
private Clock clock;
private Clock fixedClock;
मजाक:
fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();
अभिकथन:
assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock)));
मैं Clock
अपने उत्पादन कोड का उपयोग कर पाता हूं ।
आप अपने परीक्षण कोड में स्थैतिक विधि इनवोकेशन को मॉक करने के लिए JMockit या PowerMock का उपयोग कर सकते हैं । 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 : जॉन स्केट के जवाब पर टिप्पणियों को पढ़ने के बाद एसओ पर एक समान प्रश्न पर मैं अपने पिछले स्वयं से असहमत हूं। जब आप स्थैतिक तरीकों का मजाक उड़ाते हैं तो कुछ और से अधिक तर्क ने मुझे आश्वस्त किया कि आप परीक्षणों को लंबित नहीं कर सकते।
यदि आपको विरासत कोड से निपटना है, तो भी आपको स्थैतिक मॉकिंग का उपयोग करना चाहिए / कर सकते हैं।
मुझे LocalDate
इसके बजाय उदाहरण की आवश्यकता है LocalDateTime
।
ऐसे कारण से मैंने उपयोगिता वर्ग का निर्माण किया:
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);
}
}
और इसके लिए उपयोग निम्न है:
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());
}
}
आउटपुट:
2019-01-03
1998-12-12
2019-01-03
परियोजना में सभी निर्माण LocalDate.now()
को बदल Clock.getCurrentDate()
दिया।
क्योंकि यह स्प्रिंग बूट एप्लिकेशन है। test
प्रोफ़ाइल निष्पादन से पहले , सभी परीक्षणों के लिए पूर्वनिर्धारित तिथि निर्धारित करें:
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);
}
}
}
और करने के लिए जोड़ spring.factories :
org.springframework.context.ApplicationListener = com.init.TestProfileConfigurer
जोडा टाइम निश्चित रूप से अच्छा है (धन्यवाद स्टीफन, ब्रायन, आपने हमारी दुनिया को एक बेहतर जगह बना दिया है) लेकिन मुझे इसका उपयोग करने की अनुमति नहीं थी।
कुछ प्रयोग करने के बाद, मैं अंततः जावा 8 के जावा में एक विशेष तिथि के लिए समय का मजाक उड़ाने का एक तरीका लेकर आया।
यहाँ क्या किया जाना चाहिए:
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
// (...)
}
नई विशेषता 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();
}
}
// (...)
}
टेस्ट क्लास में, एक मॉक क्लॉक ऑब्जेक्ट बनाएं और परीक्षण किए गए तरीके को कॉल करने से पहले परीक्षण किए गए क्लास के इंस्टेंस में इंजेक्ट करें doExecute()
, फिर इसे ठीक बाद में रीसेट करें, जैसे:
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(..)
}
}
इसे डिबग मोड में जांचें और आप देखेंगे कि 2017 फरवरी की तारीख 3 फरवरी को सही तरीके से इंजेक्ट की गई है myService
और तुलना निर्देश में उपयोग की गई है, और फिर इसके साथ वर्तमान तिथि को ठीक से रीसेट किया गया है initDefaultClock()
।
इस उदाहरण से यह भी पता चलता है कि इंस्टेंट और लोकल टाइम को कैसे संयोजित किया जाए ( रूपांतरण के साथ समस्याओं का विस्तृत विवरण )
परीक्षण के तहत एक वर्ग
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);
}
}
एक ग्रूवी परीक्षण
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
}
}
स्प्रिंग बूट टेस्ट के लिए पावरमॉकिटो की मदद से आप मॉक मॉक कर सकते हैं ZonedDateTime
। आपको निम्नलिखित की आवश्यकता है
परीक्षण वर्ग पर आपको उस सेवा को तैयार करने की आवश्यकता है जो इसका उपयोग करता है ZonedDateTime
।
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({EscalationService.class})
@SpringBootTest
public class TestEscalationCases {
@Autowired
private EscalationService escalationService;
//...
}
परीक्षण में आप एक वांछित समय तैयार कर सकते हैं, और इसे विधि कॉल के जवाब में प्राप्त कर सकते हैं।
@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
परीक्षण में उपयोगी है, जबकिClock.system
याClock.systemUTC
आवेदन में इस्तेमाल किया जा सकता है।