यूनिट परीक्षण कि घटनाओं को सी # में उठाया जाता है (क्रम में)


160

मेरे पास कुछ कोड हैं जो PropertyChangedघटनाओं को बढ़ाते हैं और मैं इकाई परीक्षण में सक्षम होना चाहूंगा कि घटनाओं को सही ढंग से उठाया जा रहा है।

घटनाओं को बढ़ाने वाला कोड जैसा है

public class MyClass : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;  

   protected void NotifyPropertyChanged(String info)
   {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
   }  

   public string MyProperty
   {
       set
       {
           if (_myProperty != value)
           {
               _myProperty = value;
               NotifyPropertyChanged("MyProperty");
           }
       }
   }
}

मुझे अपने यूनिट परीक्षणों में निम्नलिखित कोड से एक अच्छा हरा परीक्षण मिलता है, जो प्रतिनिधियों का उपयोग करता है:

[TestMethod]
public void Test_ThatMyEventIsRaised()
{
    string actual = null;
    MyClass myClass = new MyClass();

    myClass.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
    {
         actual = e.PropertyName;
    };

    myClass.MyProperty = "testing";
    Assert.IsNotNull(actual);
    Assert.AreEqual("MyProperty", actual);
}

हालांकि, अगर मैं तब कोशिश करता हूं और एक साथ गुणों की सेटिंग को चेन करता हूं:

public string MyProperty
{
    set
    {
        if (_myProperty != value)
        {
            _myProperty = value;
            NotifyPropertyChanged("MyProperty");
            MyOtherProperty = "SomeValue";
        }
    }
}

public string MyOtherProperty
{
    set
    {
        if (_myOtherProperty != value)
        {
            _myOtherProperty = value;
            NotifyPropertyChanged("MyOtherProperty");
        }
    }
}

ईवेंट के लिए मेरा परीक्षण विफल हो जाता है - जो इवेंट कैप्चर करता है वह MyOtherProperty के लिए ईवेंट है।

मुझे पूरा यकीन है कि यह घटना आग की तरह है, मेरा UI इस तरह की प्रतिक्रिया करता है, लेकिन मेरे प्रतिनिधि केवल अंतिम घटना को आग में कैद करते हैं।

तो मैं सोच रहा हूँ:
1. क्या घटनाओं के परीक्षण का मेरा तरीका सही है?
2. जंजीर घटनाओं को बढ़ाने का मेरा तरीका सही है?

जवाबों:


190

आपके द्वारा किया गया सब कुछ सही है, बशर्ते आप चाहते हैं कि आपका परीक्षण "अंतिम घटना जो उठाई गई थी, उसे पूछें?"

आपका कोड इन दो घटनाओं को इस क्रम में निकाल रहा है

  • संपत्ति परिवर्तित (... "मेरी संपत्ति" ...)
  • संपत्ति परिवर्तित (... "MyOtherProperty" ...)

यह "सही" है या नहीं, इन घटनाओं के उद्देश्य पर निर्भर करता है।

यदि आप उठने वाली घटनाओं की संख्या का परीक्षण करना चाहते हैं, और वे जिस क्रम में उठते हैं, आप आसानी से अपने मौजूदा परीक्षण का विस्तार कर सकते हैं:

[TestMethod]
public void Test_ThatMyEventIsRaised()
{
    List<string> receivedEvents = new List<string>();
    MyClass myClass = new MyClass();

    myClass.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
    {
        receivedEvents.Add(e.PropertyName);
    };

    myClass.MyProperty = "testing";
    Assert.AreEqual(2, receivedEvents.Count);
    Assert.AreEqual("MyProperty", receivedEvents[0]);
    Assert.AreEqual("MyOtherProperty", receivedEvents[1]);
}

13
छोटा संस्करण: myClass.PropertyChanged + = (ऑब्जेक्ट प्रेषक, e) => प्राप्तEvents.Add (e.PropertyName);
श्लोमी

22

यदि आप TDD कर रहे हैं तो इवेंट टेस्टिंग बहुत दोहराव कोड उत्पन्न करना शुरू कर सकता है । मैंने एक ईवेंट मॉनिटर लिखा, जो इन स्थितियों के लिए इकाई परीक्षण लेखन के लिए बहुत अधिक स्वच्छ दृष्टिकोण को सक्षम बनाता है।

var publisher = new PropertyChangedEventPublisher();

Action test = () =>
{
    publisher.X = 1;
    publisher.Y = 2;
};

var expectedSequence = new[] { "X", "Y" };

EventMonitor.Assert(test, publisher, expectedSequence);

कृपया अधिक जानकारी के लिए निम्नलिखित के लिए मेरा जवाब देखें।

इकाई परीक्षण कि प्रतिबिंब का उपयोग करके C # में एक घटना को उठाया जाता है


3
दूसरा लिंक नीचे है।
लेन्अर्ट

10

यह बहुत पुराना है और शायद पढ़ा भी नहीं जा सकता है, लेकिन कुछ नए नए .net फीचर्स के साथ मैंने एक INPC ट्रैसर क्लास बनाया है जो इसकी अनुमति देता है:

[Test]
public void Test_Notify_Property_Changed_Fired()
{
    var p = new Project();

    var tracer = new INCPTracer();

    // One event
    tracer.With(p).CheckThat(() => p.Active = true).RaisedEvent(() => p.Active);

    // Two events in exact order
    tracer.With(p).CheckThat(() => p.Path = "test").RaisedEvent(() => p.Path).RaisedEvent(() => p.Active);
}

जिस्ट देखें: https://gist.github.com/Seikilos/6224204


सुंदर - आपको इसे पैकेजिंग पर विचार करना चाहिए और इसे nuget.org पर प्रकाशित करना चाहिए
साइमन एजिंग

1
अच्छा कार्य! मैं वास्तव में धाराप्रवाह एपीआई खोदता हूं। मैंने खुद भी ऐसा ही कुछ किया है ( github.com/f-tischler/EventTesting ) लेकिन मुझे लगता है कि आपका दृष्टिकोण और भी संक्षिप्त है।
फ्लोरियन टिस्सलर

6

नीचे थोड़ा सा परिवर्तित एंड्रयू कोड है, जो केवल उठाए गए घटनाओं के अनुक्रम को लॉग करने के बजाय गिना जाता है कि किसी विशिष्ट घटना को कितनी बार बुलाया गया है। यद्यपि यह उसके कोड पर आधारित है, मुझे यह मेरे परीक्षणों में अधिक उपयोगी लगता है।

[TestMethod]
public void Test_ThatMyEventIsRaised()
{
    Dictionary<string, int> receivedEvents = new Dictionary<string, int>();
    MyClass myClass = new MyClass();

    myClass.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
    {
        if (receivedEvents.ContainsKey(e.PropertyName))
            receivedEvents[e.PropertyName]++;
        else
            receivedEvents.Add(e.PropertyName, 1);
    };

    myClass.MyProperty = "testing";
    Assert.IsTrue(receivedEvents.ContainsKey("MyProperty"));
    Assert.AreEqual(1, receivedEvents["MyProperty"]);
    Assert.IsTrue(receivedEvents.ContainsKey("MyOtherProperty"));
    Assert.AreEqual(1, receivedEvents["MyOtherProperty"]);
}

1

इस लेख के आधार पर, मैंने यह सरल मुखर सहायक बनाया है:

private void AssertPropertyChanged<T>(T instance, Action<T> actionPropertySetter, string expectedPropertyName) where T : INotifyPropertyChanged
    {
        string actual = null;
        instance.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e)
        {
            actual = e.PropertyName;
        };
        actionPropertySetter.Invoke(instance);
        Assert.IsNotNull(actual);
        Assert.AreEqual(propertyName, actual);
    }

इस विधि सहायक के साथ, परीक्षण वास्तव में सरल हो जाता है।

[TestMethod()]
public void Event_UserName_PropertyChangedWillBeFired()
{
    var user = new User();
    AssertPropertyChanged(user, (x) => x.UserName = "Bob", "UserName");
}

1

प्रत्येक सदस्य के लिए एक परीक्षण न लिखें - यह बहुत काम है

(शायद यह समाधान हर स्थिति के लिए सही नहीं है - लेकिन यह एक संभावित तरीका दिखाता है। आपको अपने उपयोग के मामले में इसे अनुकूलित करने की आवश्यकता हो सकती है)

यह जांचने के लिए कि आपके सदस्य आपकी संपत्ति की सभी घटनाओं को सही ढंग से जवाब दे रहे हैं, पुस्तकालय में प्रतिबिंब का उपयोग करना संभव है:

  • प्रॉपर्टीचेंज इवेंट को सेटर एक्सेस पर उठाया जाता है
  • घटना को सही उठाया गया है (संपत्ति का नाम उठाया घटना के तर्क के बराबर है)

निम्न कोड का उपयोग लाइब्रेरी के रूप में किया जा सकता है और यह दिखाता है कि निम्नलिखित सामान्य वर्ग का परीक्षण कैसे किया जाए

using System.ComponentModel;
using System.Linq;

/// <summary>
/// Check if every property respons to INotifyPropertyChanged with the correct property name
/// </summary>
public static class NotificationTester
    {
        public static object GetPropertyValue(object src, string propName)
        {
            return src.GetType().GetProperty(propName).GetValue(src, null);
        }

        public static bool Verify<T>(T inputClass) where T : INotifyPropertyChanged
        {
            var properties = inputClass.GetType().GetProperties().Where(x => x.CanWrite);
            var index = 0;

            var matchedName = 0;
            inputClass.PropertyChanged += (o, e) =>
            {
                if (properties.ElementAt(index).Name == e.PropertyName)
                {
                    matchedName++;
                }

                index++;
            };

            foreach (var item in properties)
            { 
                // use setter of property
                item.SetValue(inputClass, GetPropertyValue(inputClass, item.Name));
            }

            return matchedName == properties.Count();
        }
    }

आपकी कक्षा के परीक्षण अब लिखे जा सकते हैं। (शायद आप परीक्षण को "घटना वहीं है" और "सही नाम के साथ उठाए गए घटना" में विभाजित करना चाहते हैं - आप स्वयं ऐसा कर सकते हैं)

[TestMethod]
public void EveryWriteablePropertyImplementsINotifyPropertyChangedCorrect()
{
    var viewModel = new TestMyClassWithINotifyPropertyChangedInterface();
    Assert.AreEqual(true, NotificationTester.Verify(viewModel));
}

कक्षा

using System.ComponentModel;

public class TestMyClassWithINotifyPropertyChangedInterface : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        private int id;

        public int Id
        {
            get { return id; }
            set { id = value;
                NotifyPropertyChanged("Id");
            }
        }
}

मैंने यह कोशिश की, लेकिन अगर मेरी संपत्ति बसने वालों के पास "अगर (मान == _VValue) वापसी" जैसा एक गार्ड स्टेटमेंट है, जो सभी मेरा करते हैं, तो ऊपर काम नहीं करेगा, जब तक कि मैं कुछ याद नहीं कर रहा हूं। मैं हाल ही में C ++ से C # आया हूं।
21

0

मैंने यहाँ एक विस्तार किया है:

public static class NotifyPropertyChangedExtensions
{
    private static bool _isFired = false;
    private static string _propertyName;

    public static void NotifyPropertyChangedVerificationSettingUp(this INotifyPropertyChanged notifyPropertyChanged,
      string propertyName)
    {
        _isFired = false;
        _propertyName = propertyName;
        notifyPropertyChanged.PropertyChanged += OnPropertyChanged;
    }

    private static void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == _propertyName)
        {
            _isFired = true;
        }
    }

    public static bool IsNotifyPropertyChangedFired(this INotifyPropertyChanged notifyPropertyChanged)
    {
        _propertyName = null;
        notifyPropertyChanged.PropertyChanged -= OnPropertyChanged;
        return _isFired;
    }
}

उपयोग है:

   [Fact]
    public void FilesRenameViewModel_Rename_Apply_Execute_Verify_NotifyPropertyChanged_If_Succeeded_Through_Extension_Test()
    {
        //  Arrange
        _filesViewModel.FolderPath = ConstFolderFakeName;
        _filesViewModel.OldNameToReplace = "Testing";
        //After the command's execution OnPropertyChanged for _filesViewModel.AllFilesFiltered should be raised
        _filesViewModel.NotifyPropertyChangedVerificationSettingUp(nameof(_filesViewModel.AllFilesFiltered));
        //Act
        _filesViewModel.ApplyRenamingCommand.Execute(null);
        // Assert
        Assert.True(_filesViewModel.IsNotifyPropertyChangedFired());

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