इकाई परीक्षण: DateTime.Now


164

मेरे पास कुछ यूनिट परीक्षण हैं, जो 'टाइम को वर्तमान समय' की अपेक्षा DateTime.Now से भिन्न मानते हैं और मैं कंप्यूटर के समय को बदलना नहीं चाहता, जाहिर है।

इसे प्राप्त करने के लिए सबसे अच्छी रणनीति क्या है?


वर्तमान समय में कुछ पुराने मूल्य को संग्रहीत करें और इसे datetime.now से तुलना करें, क्योंकि यूनिट परीक्षण नकली डेटा का उपयोग करता है, आप आसानी से ऐसा कर सकते हैं, है ना?
प्रशांत लखलानी

3
वर्तमान डेटटाइम की अमूर्तता प्रदान करना न केवल परीक्षण और डिबगिंग के लिए बल्कि उत्पादन कोड में भी उपयोगी है - यह आवेदन की जरूरतों पर निर्भर करता है।
पावेल होडेक


और नीचे दिए गए VirtualTime का उपयोग करते हुए एक और सरल दृष्टिकोण
शमूएल

एक विकल्प जो काम कर सकता है और आप एक अधिभार को जोड़ने में सक्षम हैं वह उस पद्धति का अधिभार बनाना है जो इसे स्वीकार करता है DateTime। यह वही होगा जो आप परीक्षण करते हैं, मौजूदा पद्धति बस अधिभार को बुलाएगी now
फिल कूपर

जवाबों:


219

सबसे अच्छी रणनीति एक अमूर्त में वर्तमान समय को लपेटने और उस अमूर्त को उपभोक्ता में इंजेक्ट करना है


वैकल्पिक रूप से , आप एक समय सारिणी को एक परिवेश संदर्भ के रूप में भी परिभाषित कर सकते हैं :

public abstract class TimeProvider
{
    private static TimeProvider current =
        DefaultTimeProvider.Instance;

    public static TimeProvider Current
    {
       get { return TimeProvider.current; }
       set 
       {
           if (value == null)
           {
               throw new ArgumentNullException("value");
           }
           TimeProvider.current = value; 
       }
   }

   public abstract DateTime UtcNow { get; }

   public static void ResetToDefault()
   {    
       TimeProvider.current = DefaultTimeProvider.Instance;
   }            
}

यह आपको इस तरह से उपभोग करने में सक्षम करेगा:

var now = TimeProvider.Current.UtcNow;

एक यूनिट टेस्ट में, आप TimeProvider.Currentटेस्ट डबल / मॉक ऑब्जेक्ट से बदल सकते हैं । उदाहरण का उपयोग कर Moq:

var timeMock = new Mock<TimeProvider>();
timeMock.SetupGet(tp => tp.UtcNow).Returns(new DateTime(2010, 3, 11));
TimeProvider.Current = timeMock.Object;

हालांकि, जब स्टेटिक स्टेट के साथ यूनिट टेस्टिंग, हमेशा कॉल करके अपनी स्थिरता को फाड़ना याद रखें TimeProvider.ResetToDefault()


7
एक परिवेशीय संदर्भ के मामले में, भले ही आप आंसू बहाते हों, आप अपने परीक्षणों को समानांतर कैसे चलाते हैं?
इल्या चेर्नोमोर्डिक

2
@IlyaChernomordik आपको आईलॉगर या अन्य क्रॉस-कटिंग कंसर्न को इंजेक्ट नहीं करना चाहिए , इसलिए एक समय प्रदाता को इंजेक्ट करना अभी भी सबसे अच्छा विकल्प है।
मार्क सेमन

5
@ माईक के रूप में मेरे जवाब में पहला वाक्य कहता है: सबसे अच्छी रणनीति यह है कि वर्तमान समय को एक अमूर्तता में लपेट दिया जाए और उपभोक्ता को उस अमूर्त में इंजेक्ट किया जाए। इस उत्तर में बाकी सब कुछ एक दूसरा सबसे अच्छा विकल्प है।
मार्क सीमन

3
नमस्ते। मैं नोब। इस समाधान के साथ DateTime.UtcNow तक पहुँचने वाले लोगों को कैसे रोका जा सकता है? किसी के लिए TimeProvider का उपयोग करना याद रखना आसान होगा। इस प्रकार परीक्षण में कीड़े के लिए अग्रणी?
Obbles

1
@ ऑबल्स कोड समीक्षाएं (या जोड़ी प्रोग्रामिंग, जो लाइव कोड समीक्षा का एक रूप है)। मुझे लगता है कि कोई ऐसा टूल लिख सकता है जो कोड बेस को DateTime.UtcNowऔर समान के लिए स्कैन करता है , लेकिन कोड समीक्षा वैसे भी एक अच्छा विचार है।
मार्क सेमन

58

ये सभी अच्छे उत्तर हैं, यही मैंने एक अलग परियोजना पर किया है:

उपयोग:

आज की वास्तविक तिथि प्राप्त करें

var today = SystemTime.Now().Date;

DateTime.Now का उपयोग करने के बजाय, आपको उपयोग करने की आवश्यकता है SystemTime.Now()... यह कठिन परिवर्तन नहीं है, लेकिन यह समाधान सभी परियोजनाओं के लिए आदर्श नहीं हो सकता है।

टाइम ट्रैवलिंग (भविष्य में 5 वर्ष का समय दें)

SystemTime.SetDateTime(today.AddYears(5));

हमारा फेक "आज" ('आज' से 5 साल हो जाएगा)

var fakeToday = SystemTime.Now().Date;

दिनांक को रीसेट करें

SystemTime.ResetDateTime();

/// <summary>
/// Used for getting DateTime.Now(), time is changeable for unit testing
/// </summary>
public static class SystemTime
{
    /// <summary> Normally this is a pass-through to DateTime.Now, but it can be overridden with SetDateTime( .. ) for testing or debugging.
    /// </summary>
    public static Func<DateTime> Now = () => DateTime.Now;

    /// <summary> Set time to return when SystemTime.Now() is called.
    /// </summary>
    public static void SetDateTime(DateTime dateTimeNow)
    {
        Now = () =>  dateTimeNow;
    }

    /// <summary> Resets SystemTime.Now() to return DateTime.Now.
    /// </summary>
    public static void ResetDateTime()
    {
        Now = () => DateTime.Now;
    }
}

1
धन्यवाद, मुझे यह कार्यात्मक aproach पसंद है। आज (दो साल बाद) मैं अभी भी TimeProvider वर्ग के संशोधित संस्करण (स्वीकृत उत्तर की जांच) का उपयोग कर रहा हूं, यह वास्तव में अच्छा काम करता है।
पेड्रो

7
@crabCRUSHERclamCOLI: यदि आपको ayende.com/blog/3408/dealing-with-time-in-tests से विचार मिला है , तो इससे लिंक करने के लिए यह एक अच्छी बात है।
जोहान गेर्ले

3
यह अवधारणा गाइड जनरेशन (पब्लिक स्टैटिक फंक <गाइड> न्यूजीड = () => गाइड.न्यूजीयूआईडी ();
mdwhatcott

2
आप इस उपयोगी विधि को भी जोड़ सकते हैं: सार्वजनिक स्थैतिक शून्य ResetDateTime (DateTime dateTimeNow) {var timespanDiff = TimeSpan.FromTicks (DateTime.Now.Ticks - dateTimeNow.Ticks); अब = () => DateTime.Now - timespanDiff; }
पावेल होडेक

17
यह बहुत खतरनाक है। यह समानांतर में चल रही अन्य इकाई परीक्षणों को प्रभावित कर सकता है।
अमेथ धन्य

24

मोल्स:

[Test]  
public void TestOfDateTime()  
{  
      var firstValue = DateTime.Now;
      MDateTime.NowGet = () => new DateTime(2000,1,1);
      var secondValue = DateTime.Now;
      Assert(firstValue > secondValue); // would be false if 'moleing' failed
}

डिस्क्लेमर - मैं मोल्स पर काम करता हूं


1
दुर्भाग्य से nunit और resharper के साथ काम नहीं करता है।
अयोध्या

ऐसा लगता है कि मोल्स अब असमर्थित हो गए हैं, उन्हें Fakes msdn.microsoft.com/en-us/library/… से बदल दिया गया है , इसमें कोई शक नहीं कि एमएस जल्द ही बंद हो जाएगा
danio

17

आपके पास इसे करने के लिए कुछ विकल्प हैं:

  1. मॉकिंग फ्रेमवर्क का उपयोग करें और DateTimeService का उपयोग करें (एक छोटा आवरण वर्ग लागू करें और इसे उत्पादन कोड पर इंजेक्ट करें)। आवरण कार्यान्वयन DateTime तक पहुंच जाएगा और परीक्षणों में आप आवरण वर्ग का मजाक उड़ा पाएंगे।

  2. टाइपमॉक आइज़ोलटर का उपयोग करें , यह DateTime.Now नकली कर सकता है और आपको परीक्षण के तहत कोड बदलने की आवश्यकता नहीं होगी।

  3. मोल्स का उपयोग करें , यह नकली DateTime भी हो सकता है। अब और उत्पादन कोड में बदलाव की आवश्यकता नहीं होगी।

कुछ उदाहरण:

Moq का उपयोग करते हुए रैपर क्लास:

[Test]
public void TestOfDateTime()
{
     var mock = new Mock<IDateTime>();
     mock.Setup(fake => fake.Now)
         .Returns(new DateTime(2000, 1, 1));

     var result = new UnderTest(mock.Object).CalculateSomethingBasedOnDate();
}

public class DateTimeWrapper : IDateTime
{
      public DateTime Now { get { return DateTime.Now; } }
}

आइसोलेटर का उपयोग करके सीधे डेटाइम को भूनना:

[Test]
public void TestOfDateTime()
{
     Isolate.WhenCalled(() => DateTime.Now).WillReturn(new DateTime(2000, 1, 1));

     var result = new UnderTest().CalculateSomethingBasedOnDate();
}

डिस्क्लेमर - मैं टाइपमॉक पर काम करता हूं


12

सिस्टम के लिए एक नकली असेंबली जोड़ें (सिस्टम रेफरेंस पर राइट क्लिक करें> फर्जी असेंबली जोड़ें)।

और अपनी परीक्षा विधि में लिखें:

using (ShimsContext.Create())
{
   System.Fakes.ShimDateTime.NowGet = () => new DateTime(2014, 3, 10);
   MethodThatUsesDateTimeNow();
}

यदि आपके पास Vs प्रीमियम या अंतिम तक पहुंच है, तो यह इसे करने का सबसे आसान / सबसे तेज़ तरीका है।
रग्डर

7

@Crabcrusherclamcollector उत्तर के बारे में EF क्वेरीज़ में उस दृष्टिकोण का उपयोग करते समय समस्या होती है (System.NotSupportedException: LINQ अभिव्यक्ति नोड प्रकार 'Invoke' LINQ से संस्थाओं में समर्थित नहीं है)। मैंने उस पर कार्यान्वयन संशोधित किया है:

public static class SystemTime
    {
        private static Func<DateTime> UtcNowFunc = () => DateTime.UtcNow;

        public static void SetDateTime(DateTime dateTimeNow)
        {
            UtcNowFunc = () => dateTimeNow;
        }

        public static void ResetDateTime()
        {
            UtcNowFunc = () => DateTime.UtcNow;
        }

        public static DateTime UtcNow
        {
            get
            {
                DateTime now = UtcNowFunc.Invoke();
                return now;
            }
        }
    }

मैं ईएफ द्वारा अनुमान लगा रहा हूं कि आपका मतलब एंटिटी फ्रेमवर्क है? सिस्टमटाइम आप जिस वस्तु का उपयोग कर रहे हैं वह कैसा दिखता है? मेरा अनुमान है कि आपके पास वास्तविक इकाई पर SystemTime का संदर्भ है, इसके बजाय आपको अपनी इकाई पर एक नियमित DateTime ऑब्जेक्ट का उपयोग करना चाहिए और जहाँ भी यह आपके आवेदन में समझ में आता है, उसे SystemTime का उपयोग करके सेट करें
crabCRUSHERclamCOLLECTOR

1
हां मैंने linq query और EntityFramework6 बनाते समय और SystemTime से उस क्वेरी UtcNow में संदर्भित करते हुए इसका परीक्षण किया है। वर्णित के रूप में अपवाद था। कार्यान्वयन बदलने के बाद यह अच्छी तरह से काम करता है।
marcinn

मैं इस समाधान से प्यार करता हूँ! महान!
मिरिंड 4

7

एक कोड का परीक्षण करने के लिए System.DateTime, जिस पर निर्भर करता है , का system.dllमज़ाक़ उड़ाया जाना चाहिए।

वहाँ दो रूपरेखा है कि मुझे पता है कि यह करता है। Microsoft फ़ेक और स्मॉक्स

Microsoft फेक को विजुअल स्टूडियो 2012 अल्टीमेटम की आवश्यकता होती है और यह सीधे कॉम्पटन से बाहर काम करता है।

स्मोक्स एक खुला स्रोत है और उपयोग करने में बहुत आसान है। इसे NuGet का उपयोग करके डाउनलोड किया जा सकता है।

निम्नलिखित एक नकली दिखाता है System.DateTime:

Smock.Run(context =>
{
  context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));

   // Outputs "2000"
   Console.WriteLine(DateTime.Now.Year);
});

5

एक धागा सुरक्षित SystemClockका उपयोग करते हुए ThreadLocal<T>मेरे लिए महान काम करता है।

ThreadLocal<T> .Net फ्रेमवर्क v4.0 और उच्चतर में उपलब्ध है।

/// <summary>
/// Provides access to system time while allowing it to be set to a fixed <see cref="DateTime"/> value.
/// </summary>
/// <remarks>
/// This class is thread safe.
/// </remarks>
public static class SystemClock
{
    private static readonly ThreadLocal<Func<DateTime>> _getTime =
        new ThreadLocal<Func<DateTime>>(() => () => DateTime.Now);

    /// <inheritdoc cref="DateTime.Today"/>
    public static DateTime Today
    {
        get { return _getTime.Value().Date; }
    }

    /// <inheritdoc cref="DateTime.Now"/>
    public static DateTime Now
    {
        get { return _getTime.Value(); }
    }

    /// <inheritdoc cref="DateTime.UtcNow"/>
    public static DateTime UtcNow
    {
        get { return _getTime.Value().ToUniversalTime(); }
    }

    /// <summary>
    /// Sets a fixed (deterministic) time for the current thread to return by <see cref="SystemClock"/>.
    /// </summary>
    public static void Set(DateTime time)
    {
        if (time.Kind != DateTimeKind.Local)
            time = time.ToLocalTime();

        _getTime.Value = () => time;
    }

    /// <summary>
    /// Resets <see cref="SystemClock"/> to return the current <see cref="DateTime.Now"/>.
    /// </summary>
    public static void Reset()
    {
        _getTime.Value = () => DateTime.Now;
    }
}

उपयोग उदाहरण:

[TestMethod]
public void Today()
{
    SystemClock.Set(new DateTime(2015, 4, 3));

    DateTime expectedDay = new DateTime(2015, 4, 2);
    DateTime yesterday = SystemClock.Today.AddDays(-1D);
    Assert.AreEqual(expectedDay, yesterday);

    SystemClock.Reset();
}

1
+! थ्रेड लोकल को समानांतर परीक्षण निष्पादन और कई परीक्षण संभावित रूप से वर्तमान Nowउदाहरण को ओवरराइड करने वाले मुद्दों से बचना चाहिए ।
हक्स

1
इस का उपयोग करने की कोशिश की, लेकिन धागे के बीच एक मुद्दा था तो एक समान प्रिंसिपल के साथ चले गए लेकिन AsyncLocalइसके बजाय का उपयोग करते हुएThreadLocal
rrrr

@rrr: क्या आप उस मुद्दे की व्याख्या कर सकते हैं जो आपके पास था?
हेंक वैन बोइजेन

@HenkvanBoeijen ज़रूर, हम समय सेट होने पर मुद्दों में भाग गए और फिर हमें एक async विधि की प्रतीक्षा थी। Console.WriteLine(SystemClock.Now); SystemClock.Set(new DateTime(2017, 01, 01)); Console.WriteLine(SystemClock.Now); await Task.Delay(1000); Console.WriteLine(SystemClock.Now);
rrrr

@HenkvanBoeijen मैंने जो उपयोग किया है, उसके साथ एक उत्तर जोड़ा है लिंक
rrrr

3

मैं इसी मुद्दे पर भागा, लेकिन Microsoft से एक शोध परियोजना मिली जो इस मुद्दे को हल करती है।

http://research.microsoft.com/en-us/projects/moles/

मोल्स परीक्षण स्टब्स के लिए एक हल्का ढांचा है और .NET में detours है जो प्रतिनिधियों पर आधारित है। मोल्स का उपयोग किसी भी .NET विधि को अलग करने के लिए किया जा सकता है, जिसमें गैर-आभासी / स्थिर तरीके सील किए गए प्रकारों में शामिल हैं

// Let's detour DateTime.Now
MDateTime.NowGet = () => new DateTime(2000,1, 1);

if (DateTime.Now == new DateTime(2000, 1, 1);
{
    throw new Exception("Wahoo we did it!");
}

नमूना कोड को मूल से संशोधित किया गया था।

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


2
BTW, यह केवल VS2010 के साथ काम करता है। मैं परेशान था जब मैंने पाया कि Moles अब VS2012 के लिए फेक है और यह केवल प्रीमियम और अल्टीमेट विजुअल स्टूडियो के लिए उपलब्ध है। वीएस 2012 पेशेवर के लिए कोई नकली। इसलिए मुझे वास्तव में इसका उपयोग करने के लिए कभी नहीं मिला। :(
बॉबी तोप

3

टाइपमॉक केDateTime.Now साथ मजाक करने पर एक विशेष नोट ...

का मूल्य DateTime.Nowइसे ठीक से मॉक किया जाए, इसके लिए एक वैरिएबल के को एक चर में रखा जाना चाहिए। उदाहरण के लिए:

यह काम नहीं करता:

if ((DateTime.Now - message.TimeOpened.Value) > new TimeSpan(1, 0, 0))

हालाँकि, यह करता है:

var currentDateTime = DateTime.Now;
if ((currentDateTime - message.TimeOpened.Value) > new TimeSpan(1, 0, 0))

2

नकली वस्तु।

एक नकली डेटाइम जो अब लौटाता है जो आपके परीक्षण के लिए उपयुक्त है।


2

मुझे आश्चर्य है कि किसी ने भी जाने के सबसे स्पष्ट तरीकों में से एक का सुझाव नहीं दिया है:

public class TimeDependentClass
{
    public void TimeDependentMethod(DateTime someTime)
    {
        if (GetCurrentTime() > someTime) DoSomething();
    }

    protected virtual DateTime GetCurrentTime()
    {
        return DateTime.Now; // or UtcNow
    }
}

तो आप बस अपने परीक्षण डबल में इस विधि को ओवरराइड कर सकते हैं।

मुझे TimeProviderकुछ मामलों में एक वर्ग को इंजेक्शन देना भी पसंद है , लेकिन दूसरों के लिए, यह पर्याप्त से अधिक है। TimeProviderयदि आप इसे कई वर्गों में पुन: उपयोग करने की आवश्यकता है, तो मैं शायद संस्करण का पक्ष लूंगा।

EDIT: रुचि रखने वाले के लिए, इसे आपकी कक्षा में एक "सीम" जोड़ना कहा जाता है, एक बिंदु जहां आप इसे संशोधित करने के लिए व्यवहार के लिए हुक कर सकते हैं (परीक्षण प्रयोजनों के लिए या अन्यथा) बिना वास्तव में कक्षा में कोड बदलने के लिए।


1

अच्छा अभ्यास है, जब DateTimeProvider IDisposable को लागू करता है।

public class DateTimeProvider : IDisposable 
{ 
    [ThreadStatic] 
    private static DateTime? _injectedDateTime; 

    private DateTimeProvider() 
    { 
    } 

    /// <summary> 
    /// Gets DateTime now. 
    /// </summary> 
    /// <value> 
    /// The DateTime now. 
    /// </value> 
    public static DateTime Now 
    { 
        get 
        { 
            return _injectedDateTime ?? DateTime.Now; 
        } 
    } 

    /// <summary> 
    /// Injects the actual date time. 
    /// </summary> 
    /// <param name="actualDateTime">The actual date time.</param> 
    public static IDisposable InjectActualDateTime(DateTime actualDateTime) 
    { 
        _injectedDateTime = actualDateTime; 

        return new DateTimeProvider(); 
    } 

    public void Dispose() 
    { 
        _injectedDateTime = null; 
    } 
} 

इसके बाद, आप यूनिट परीक्षणों के लिए अपने नकली डेटाइम को इंजेक्ट कर सकते हैं

    using (var date = DateTimeProvider.InjectActualDateTime(expectedDateTime)) 
    { 
        var bankAccount = new BankAccount(); 

        bankAccount.DepositMoney(600); 

        var lastTransaction = bankAccount.Transactions.Last(); 

        Assert.IsTrue(expectedDateTime.Equals(bankAccount.Transactions[0].TransactionDate)); 
    } 

उदाहरण देखें DateTimeProvider का उदाहरण


1

ऐसा करने का एक साफ तरीका है वर्चुअरमेट को इंजेक्ट करना। यह आपको समय को नियंत्रित करने की अनुमति देता है। पहले VirtualTime स्थापित करें

Install-Package VirtualTime

उदाहरण के लिए, DateTime.Now या UtcNow के लिए सभी कॉल पर 5 गुना तेजी से आगे बढ़ने के लिए अनुमति देता है

var DateTime = DateTime.Now.ToVirtualTime(5);

समय को धीमा करने के लिए, जैसे 5 बार धीमी गति से करें

var DateTime = DateTime.Now.ToVirtualTime(0.5);

टाइम स्टैंड बनाने के लिए अभी भी करते हैं

var DateTime = DateTime.Now.ToVirtualTime(0);

समय में पीछे की ओर बढ़ना अभी तक परीक्षण नहीं किया गया है

यहाँ एक नमूना परीक्षण कर रहे हैं:

[TestMethod]
public void it_should_make_time_move_faster()
{
    int speedOfTimePerMs = 1000;
    int timeToPassMs = 3000;
    int expectedElapsedVirtualTime = speedOfTimePerMs * timeToPassMs;
    DateTime whenTimeStarts = DateTime.Now;
    ITime time = whenTimeStarts.ToVirtualTime(speedOfTimePerMs);
    Thread.Sleep(timeToPassMs);
    DateTime expectedTime = DateTime.Now.AddMilliseconds(expectedElapsedVirtualTime - timeToPassMs);
    DateTime virtualTime = time.Now;

    Assert.IsTrue(TestHelper.AreEqualWithinMarginOfError(expectedTime, virtualTime, MarginOfErrorMs));
}

आप यहां और परीक्षण देख सकते हैं:

https://github.com/VirtualTime/VirtualTime/blob/master/VirtualTimeLib.Tests/when_virtual_time_is_used.cs

क्या DateTime.Now.ToVirtualTime एक्सटेंशन आपको आईटीआई का एक उदाहरण देता है, जिसे आप एक विधि / कक्षा में पास करते हैं जो आईटीईएम पर निर्भर करता है। कुछ DateTime.Now.ToVirtualTime अपनी पसंद के DI कंटेनर में सेटअप है

यहाँ एक और उदाहरण एक वर्ग के अविश्वास में इंजेक्शन है

public class AlarmClock
{
    private ITime DateTime;
    public AlarmClock(ITime dateTime, int numberOfHours)
    {
        DateTime = dateTime;
        SetTime = DateTime.UtcNow.AddHours(numberOfHours);
        Task.Run(() =>
        {
            while (!IsAlarmOn)
            {
                IsAlarmOn = (SetTime - DateTime.UtcNow).TotalMilliseconds < 0;
            }
        });
    }
    public DateTime SetTime { get; set; }
    public bool IsAlarmOn { get; set; }
}

[TestMethod]
public void it_can_be_injected_as_a_dependency()
{
    //virtual time has to be 1000*3.75 faster to get to an hour 
    //in 1000 ms real time
    var dateTime = DateTime.Now.ToVirtualTime(1000 * 3.75);
    var numberOfHoursBeforeAlarmSounds = 1;
    var alarmClock = new AlarmClock(dateTime, numberOfHoursBeforeAlarmSounds);
    Assert.IsFalse(alarmClock.IsAlarmOn);
    System.Threading.Thread.Sleep(1000);
    Assert.IsTrue(alarmClock.IsAlarmOn);
}

परीक्षण कोड के बारे में भी एक अच्छी रीडिंग है जो आयेंडे द्वारा DateTime पर निर्भर करती है यहाँ ayende.com/blog/3408/dealing-with-time-in-tests
शमूएल

1

हम एक स्थिर SystemTime ऑब्जेक्ट का उपयोग कर रहे थे, लेकिन समानांतर इकाई परीक्षण चलाने में समस्याओं में भाग गया। मैंने हेंक वैन बोइजेन के समाधान का उपयोग करने का प्रयास किया, लेकिन असमान सिंक्रोनस थ्रेड्स में समस्याएं थीं, नीचे दिए गए तरीके से एसिंक्लोक्लॉक का उपयोग करके समाप्त हो गया:

public static class Clock
{
    private static Func<DateTime> _utcNow = () => DateTime.UtcNow;

    static AsyncLocal<Func<DateTime>> _override = new AsyncLocal<Func<DateTime>>();

    public static DateTime UtcNow => (_override.Value ?? _utcNow)();

    public static void Set(Func<DateTime> func)
    {
        _override.Value = func;
    }

    public static void Reset()
    {
        _override.Value = null;
    }
}

से प्राप्त https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08


1

एक पुराना सवाल है, लेकिन अभी भी मान्य है।

मेरा दृष्टिकोण System.DateTime.Nowकॉल को लपेटने के लिए एक नया इंटरफ़ेस और वर्ग बनाना है

public interface INow
{
    DateTime Execute();
}

public sealed class Now : INow
{
    public DateTime Execute()
    {
        return DateTime.Now
    }
}

यह इंटरफ़ेस किसी भी वर्ग में इंजेक्ट किया जा सकता है जिसे वर्तमान दिनांक और समय प्राप्त करने की आवश्यकता है। इस उदाहरण में, मेरे पास एक वर्ग है जो वर्तमान समय और समय (एक इकाई परीक्षण योग्य System.DateTime.Now.Add (TimeSpan) के लिए एक टाइमपैन जोड़ता है

public interface IAddTimeSpanToCurrentDateAndTime
{
    DateTime Execute(TimeSpan input);
}

public class AddTimeSpanToCurrentDateAndTime : IAddTimeSpanToCurrentDateAndTime
{
    private readonly INow _now;

    public AddTimeSpanToCurrentDateAndTime(INow now)
    {
        this._now = now;
    }

    public DateTime Execute(TimeSpan input)
    {
        var currentDateAndTime = this._now.Execute();

        return currentDateAndTime.Add(input);
    }
}

और परीक्षण यह सुनिश्चित करने के लिए लिखा जा सकता है कि यह सही ढंग से कार्य करता है। मैं NUnit और Moq का उपयोग कर रहा हूं, लेकिन कोई भी परीक्षण ढांचा करेगा

public class Execute
{
    private Moq.Mock<INow> _nowMock;

    private AddTimeSpanToCurrentDateAndTime _systemUnderTest;

    [SetUp]
    public void Initialize()
    {
        this._nowMock = new Moq.Mock<INow>(Moq.MockBehavior.Strict);

        this._systemUnderTest = AddTimeSpanToCurrentDateAndTime(
            this._nowMock.Object);
    }

    [Test]
    public void AddTimeSpanToCurrentDateAndTimeExecute0001()
    {
        // arrange

        var input = new TimeSpan(911252);

        // arrange : mocks

        this._nowMock
            .Setup(a => a.Execute())
            .Returns(new DateTime(348756););

        // arrange : expected

        var expected = new DateTime(911252 + 348756);

        // act

        var actual = this._systemUnderTest.Execute(input).Result;

        // assert

        Assert.Equals(actual, expected);
    }
}

यह पैटर्न किसी भी कार्य करता है जो बाह्य कारकों, जैसे पर निर्भर के लिए काम करेंगे System.Random.Next(), System.DateTime.Now.UtcNow, System.Guid.NewGuid()आदि

अधिक उदाहरणों के लिए https://loadlimited.visualstudio.com/Stamina/_git/Stamina.Core देखें या https://www.nuget.org/packages/Stamina.Core नगेट पैकेज प्राप्त करें


1

आप वर्ग को बदल सकते हैं यदि आप एक का उपयोग करने के परीक्षण कर रहे हैं Func<DateTime>यह निर्माता मानकों के माध्यम से जो पारित हो जाएगा, इसलिए जब आप वास्तविक कोड में वर्ग के उदाहरण बनाते हैं, आप पास कर सकते हैं () => DateTime.UtcNowकरने के लिएFunc<DateTime> पैरामीटर , और परीक्षण पर, आप समय पास कर सकते हैं परीक्षण करना चाहते हैं।

उदाहरण के लिए:

    [TestMethod]
    public void MyTestMethod()
    {
        var instance = new MyClass(() => DateTime.MinValue);
        Assert.AreEqual(instance.MyMethod(), DateTime.MinValue);
    } 

    public void RealWorldInitialization()
    {
        new MyClass(() => DateTime.UtcNow);
    }

    class MyClass
    {
        private readonly Func<DateTime> _utcTimeNow;

        public MyClass(Func<DateTime> UtcTimeNow)
        {
            _utcTimeNow = UtcTimeNow;
        }

        public DateTime MyMethod()
        {
            return _utcTimeNow();
        }
    }

1
मुझे यह उत्तर पसंद है। हालांकि मैंने जो कुछ भी इकट्ठा किया है, वह सी # दुनिया में आंखें बढ़ाने वाला हो सकता है, जहां कक्षाओं / वस्तुओं पर अधिक ध्यान दिया जाता है। किसी भी तरह से, मैं इसे पसंद करता हूं क्योंकि यह वास्तव में सरल है और मेरे लिए स्पष्ट है कि क्या हो रहा है।
छद्मलेबल

0

मुझे भी यही समस्या हुई, लेकिन मैं सोच रहा था कि हमें एक ही कक्षा में सेट डेटाइम चीजों का उपयोग नहीं करना चाहिए। क्योंकि यह एक दिन का दुरुपयोग कर सकता है। इसलिए मैंने प्रदाता का उपयोग किया है

public class DateTimeProvider
{
    protected static DateTime? DateTimeNow;
    protected static DateTime? DateTimeUtcNow;

    public DateTime Now
    {
        get
        {
            return DateTimeNow ?? System.DateTime.Now;
        }
    }

    public DateTime UtcNow
    {
        get
        {
            return DateTimeUtcNow ?? System.DateTime.UtcNow;
        }
    }

    public static DateTimeProvider DateTime
    {
        get
        {
            return new DateTimeProvider();
        }
    }

    protected DateTimeProvider()
    {       
    }
}

परीक्षण के लिए, परीक्षण परियोजना में एक सहायक बनाया गया जो कि निर्धारित चीजों से निपटेगा,

public class MockDateTimeProvider : DateTimeProvider
{
    public static void SetNow(DateTime now)
    {
        DateTimeNow = now;
    }

    public static void SetUtcNow(DateTime utc)
    {
        DateTimeUtcNow = utc;
    }

    public static void RestoreAsDefault()
    {
        DateTimeNow = null;
        DateTimeUtcNow = null;
    }
}

कोड पर

var dateTimeNow = DateTimeProvider.DateTime.Now         //not DateTime.Now
var dateTimeUtcNow = DateTimeProvider.DateTime.UtcNow   //not DateTime.UtcNow

और परीक्षणों पर

[Test]
public void Mocked_Now()
{
    DateTime now = DateTime.Now;
    MockDateTimeProvider.SetNow(now);    //set to mock
    Assert.AreEqual(now, DateTimeProvider.DateTime.Now);
    Assert.AreNotEqual(now, DateTimeProvider.DateTime.UtcNow);
}

[Test]
public void Mocked_UtcNow()
{
    DateTime utcNow = DateTime.UtcNow;
    MockDateTimeProvider.SetUtcNow(utcNow);   //set to mock
    Assert.AreEqual(utcNow, DateTimeProvider.DateTime.UtcNow);
    Assert.AreNotEqual(utcNow, DateTimeProvider.DateTime.Now);
}

लेकिन एक बात याद रखने की जरूरत है, कुछ समय में वास्तविक DateTime और प्रदाता की DateTime एक ही कार्य नहीं करती है

[Test]
public void Now()
{
    Assert.AreEqual(DateTime.Now.Kind, DateTimeProvider.DateTime.Now.Kind);
    Assert.LessOrEqual(DateTime.Now, DateTimeProvider.DateTime.Now);
    Assert.LessOrEqual(DateTimeProvider.DateTime.Now - DateTime.Now, TimeSpan.FromMilliseconds(1));
}

मैंने मान लिया कि डिफेंस अधिकतम TimeSpan होगा । FromMilliseconds (0.00002) । लेकिन ज्यादातर समय यह कम है

MockSamples पर नमूना खोजें


0

यहाँ मेरे इस सवाल का एवर है। मैं 'एम्बिएंट कॉन्टेक्स्ट' पैटर्न को आइडीसॉपी के साथ मिलाता हूं। तो आप अपने सामान्य प्रोग्राम कोड में DateTimeProvider.Current का उपयोग कर सकते हैं और परीक्षण में आप एक उपयोग कथन के साथ गुंजाइश को ओवरराइड कर सकते हैं।

using System;
using System.Collections.Immutable;


namespace ambientcontext {

public abstract class DateTimeProvider : IDisposable
{
    private static ImmutableStack<DateTimeProvider> stack = ImmutableStack<DateTimeProvider>.Empty.Push(new DefaultDateTimeProvider());

    protected DateTimeProvider()
    {
        if (this.GetType() != typeof(DefaultDateTimeProvider))
            stack = stack.Push(this);
    }

    public static DateTimeProvider Current => stack.Peek();
    public abstract DateTime Today { get; }
    public abstract DateTime Now {get; }

    public void Dispose()
    {
        if (this.GetType() != typeof(DefaultDateTimeProvider))
            stack = stack.Pop();
    }

    // Not visible Default Implementation 
    private class DefaultDateTimeProvider : DateTimeProvider {
        public override DateTime Today => DateTime.Today; 
        public override DateTime Now => DateTime.Now; 
    }
}
}

एक यूनिट-टेस्ट के अंदर उपरोक्त DateTimeProvider का उपयोग कैसे किया जाता है

using System;
using Xunit;

namespace ambientcontext
{
    public class TestDateTimeProvider
    {
        [Fact]
        public void TestDateTime()
        {
            var actual = DateTimeProvider.Current.Today;
            var expected = DateTime.Today;

            Assert.Equal<DateTime>(expected, actual);

            using (new MyDateTimeProvider(new DateTime(2012,12,21)))
            {
                Assert.Equal(2012, DateTimeProvider.Current.Today.Year);

                using (new MyDateTimeProvider(new DateTime(1984,4,4)))
                {
                    Assert.Equal(1984, DateTimeProvider.Current.Today.Year);    
                }

                Assert.Equal(2012, DateTimeProvider.Current.Today.Year);
            }

            // Fall-Back to Default DateTimeProvider 
            Assert.Equal<int>(expected.Year,  DateTimeProvider.Current.Today.Year);
        }

        private class MyDateTimeProvider : DateTimeProvider 
        {
            private readonly DateTime dateTime; 

            public MyDateTimeProvider(DateTime dateTime):base()
            {
                this.dateTime = dateTime; 
            }

            public override DateTime Today => this.dateTime.Date;

            public override DateTime Now => this.dateTime;
        }
    }
}

0

उपयोग ITimeProviderकरने के लिए हमें इसे विशेष साझा साझा परियोजना में ले जाने के लिए मजबूर किया गया जिसे बाकी अन्य परियोजनाओं से संदर्भित किया जाना चाहिए। लेकिन यह निर्भरता के नियंत्रण को जटिल बनाता है

हमने ITimeProvider.NET फ्रेमवर्क में खोज की है। हमने NuGet पैकेज की खोज की, और ऐसा पाया जो काम नहीं कर सकता है DateTimeOffset

इसलिए हम अपने स्वयं के समाधान के साथ आए, जो केवल मानक पुस्तकालय के प्रकारों पर निर्भर करता है। हम एक उदाहरण का उपयोग कर रहे हैं Func<DateTimeOffset>

कैसे इस्तेमाल करे

public class ThingThatNeedsTimeProvider
{
    private readonly Func<DateTimeOffset> now;
    private int nextId;

    public ThingThatNeedsTimeProvider(Func<DateTimeOffset> now)
    {
        this.now = now;
        this.nextId = 1;
    }

    public (int Id, DateTimeOffset CreatedAt) MakeIllustratingTuple()
    {
        return (nextId++, now());
    }
}

कैसे पंजीकृत करें

Autofac

builder.RegisterInstance<Func<DateTimeOffset>>(() => DateTimeOffset.Now);

( भविष्य के संपादकों के लिए: अपने मामलों को यहां जोड़ें )।

कैसे करें यूनिट टेस्ट

public void MakeIllustratingTuple_WhenCalled_FillsCreatedAt()
{
    DateTimeOffset expected = CreateRandomDateTimeOffset();
    DateTimeOffset StubNow() => expected;
    var thing = new ThingThatNeedsTimeProvider(StubNow);

    var (_, actual) = thing.MakeIllustratingTuple();

    Assert.AreEqual(expected, actual);
}

0

हो सकता है कि कम पेशेवर लेकिन सरल समाधान उपभोक्ता विधि पर एक डेटटाइम पैरामीटर बना सकते हैं। उदाहरण के लिए नमूना मेथोड की तरह विधि बनाने के बजाय, नमूनामाथोड 1 को पैरामीटर के साथ बनाएं। नमूनामेथोड 1 का परीक्षण आसान है

public void SampleMethod()
    {
        DateTime anotherDateTime = DateTime.Today.AddDays(-10);
        if ((DateTime.Now-anotherDateTime).TotalDays>10)
        {

        }
    }
    public void SampleMethod1(DateTime dateTimeNow)
    {
        DateTime anotherDateTime = DateTime.Today.AddDays(-10);
        if ((dateTimeNow - anotherDateTime).TotalDays > 10)
        {

        }

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