it-swarm.com.de

Komponententest, bei dem Ereignisse in C # ausgelöst werden (in Reihenfolge)

Ich habe einen Code, der PropertyChanged Ereignisse auslöst, und ich möchte in der Lage sein, zu testen, ob die Ereignisse korrekt ausgelöst werden.

Der Code, der die Ereignisse auslöst, ist wie folgt

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

Aus dem folgenden Code in meinen Unit-Tests, in denen Delegierte verwendet werden, erhalte ich einen Nizza-Grün-Test:

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

Wenn ich dann jedoch versuche, die Einstellung der Eigenschaften wie folgt zu verketten:

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

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

Mein Test für das Ereignis schlägt fehl - das erfasste Ereignis ist das Ereignis für MyOtherProperty.

Ich bin mir ziemlich sicher, dass das Ereignis ausgelöst wird. Meine Benutzeroberfläche reagiert genauso, aber mein Delegat erfasst nur das letzte Ereignis, das ausgelöst werden soll.

Also frage ich mich:
1. Ist meine Methode zum Testen von Ereignissen korrekt?
2. Ist meine Methode zum Auslösen von angekettet Ereignissen korrekt?

150
David Hall

Alles, was Sie getan haben, ist korrekt, vorausgesetzt, Sie möchten, dass Ihr Test die Frage "Was ist das letzte Ereignis, das ausgelöst wurde?"

Ihr Code löst diese beiden Ereignisse in dieser Reihenfolge aus

  • Eigenschaft geändert (... "Meine Eigenschaft" ...)
  • Eigenschaft geändert (... "MyOtherProperty" ...)

Ob dies "richtig" ist oder nicht, hängt vom Zweck dieser Ereignisse ab.

Wenn Sie die Anzahl der ausgelösten Ereignisse und die Reihenfolge ihrer Auslösung testen möchten, können Sie Ihren vorhandenen Test problemlos erweitern:

[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]);
}
176

Wenn Sie TDD durchführen, können Ereignistests beginnen, eine Menge sich wiederholenden Codes zu generieren. Ich habe einen Ereignismonitor geschrieben, mit dem das Schreiben von Komponententests für diese Situationen wesentlich übersichtlicher gestaltet werden kann.

var publisher = new PropertyChangedEventPublisher();

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

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

EventMonitor.Assert(test, publisher, expectedSequence);

Bitte beachten Sie meine Antwort auf das Folgende für weitere Details.

nit testet, dass ein Ereignis in C # mit Reflection ausgelöst wird

19
Tim Lloyd

Dies ist sehr alt und wird wahrscheinlich nicht einmal gelesen, aber mit einigen coolen neuen .net-Funktionen habe ich eine INPC-Tracer-Klasse erstellt, die Folgendes ermöglicht:

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

Siehe Gist: https://Gist.github.com/Seikilos/6224204

8
Samuel

Im Folgenden finden Sie einen leicht geänderten Andrews-Code, der anstelle der Protokollierung der Abfolge der ausgelösten Ereignisse zählt, wie oft ein bestimmtes Ereignis aufgerufen wurde. Obwohl es auf seinem Code basiert, finde ich es in meinen Tests nützlicher.

[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"]);
}
5
Damir Arh

Basierend auf diesem Artikel habe ich diesen einfachen Assertion-Helfer erstellt:

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

Mit diesem Methoden-Helfer wird der Test wirklich einfach.

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

Schreiben Sie nicht für jedes Mitglied einen Test - das ist viel Arbeit

(Vielleicht ist diese Lösung nicht für jede Situation perfekt - aber sie zeigt einen möglichen Weg auf. Möglicherweise müssen Sie sie an Ihren Anwendungsfall anpassen.)

Es ist möglich, Reflection in einer Bibliothek zu verwenden, um zu testen, ob alle Mitglieder auf das Ereignis "Eigenschaftsänderung" richtig reagieren:

  • Das PropertyChanged-Ereignis wird beim Zugriff auf den Setter ausgelöst
  • Ereignis wird korrekt ausgelöst (Name der Eigenschaft entspricht Argument des ausgelösten Ereignisses)

Der folgende Code kann als Bibliothek verwendet werden und zeigt, wie die folgende generische Klasse getestet wird

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

Die Tests Ihrer Klasse können jetzt als geschrieben werden. (Vielleicht möchten Sie den Test in "event is there" und "event raised with correct name" aufteilen - Sie können dies selbst tun.)

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

Klasse

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");
            }
        }
}
1
WhileTrueSleep

Ich habe hier eine Erweiterung vorgenommen:

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

Es gibt die Verwendung:

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

    }
0
Mr.B