2

Ich habe eine einfache Klasse, die INotifyPropertyChanged implementiert, ich die Änderung der Eigenschaft auf einem anderen Thread aufrufen, und ich hatte eine ziemlich harte Zeit FluentAsserts zu sehen, dass die propertyChanged wurde aufgerufen. Es scheint nicht zu passieren, wenn ich eine Task.Delay in einer async Task Methode verwende. Aber wenn ich nur den Faden schlafe.Fluent-Assertions ShouldRaisePropertyChangeFor funktioniert nicht für asynchrone Aufgaben?

Die SimpleNotify Klasse:

namespace FluentAssertPropertyThreads 
{ 
    class SimpleNotify : System.ComponentModel.INotifyPropertyChanged 
    { 
     public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 
     private void onChange(string name) 
     { 
      this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name)); 
     } 

     private int count = 0; 
     public int Count 
     { 
      get 
      { 
       return this.count; 
      } 

      set 
      { 
       if (this.count != value) 
       { 
        this.count = value; 
        this.onChange(nameof(this.Count)); 
       } 
      } 
     } 
    } 
} 

und hier sind meine Unit-Tests:

using FluentAssertions; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace FluentAssertPropertyThreads 
{ 
    [TestClass] 
    public class UnitTest1 
    { 
     private SimpleNotify simpleNotify; 
     private int notifyCount; 
     private void bumpCount() 
     { 
      this.simpleNotify.Count++; 
     } 

     private void SimpleNotify_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
     { 
      SimpleNotify simpleNotify = sender as SimpleNotify; 
      if (simpleNotify == null) 
      { 
       throw new ArgumentNullException(nameof(sender), "sender should be " + nameof(SimpleNotify)); 
      } 

      if (e.PropertyName.Equals(nameof(SimpleNotify.Count), StringComparison.InvariantCultureIgnoreCase)) 
      { 
       this.notifyCount++; 
      } 
     } 

     [TestInitialize] 
     public void TestSetup() 
     { 
      this.notifyCount = 0; 
      this.simpleNotify = new SimpleNotify(); 
      this.simpleNotify.PropertyChanged += SimpleNotify_PropertyChanged; 
      this.simpleNotify.MonitorEvents(); 

      Thread thread = new Thread(this.bumpCount) 
      { 
       IsBackground = true, 
       Name = @"My Background Thread", 
       Priority = ThreadPriority.Normal 
      }; 
      thread.Start(); 
     } 

     [TestMethod] 
     public async Task TestMethod1() 
     { 
      await Task.Delay(100); 
      this.notifyCount.Should().Be(1);  //this passes, so I know that my notification has be executed. 
      this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count);  //but this fails, saying that I need to be monitoring the events (which I am above) 
     } 

     [TestMethod] 
     public void TestMethod2() 
     { 
      Thread.Sleep(100); 
      this.notifyCount.Should().Be(1);  //this passes, so I know that my notification has be executed. 
      this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count);  //this passes as I expected 
     } 
    } 
} 

Der genaue Fehler ist:

System.InvalidOperationException: Das Objekt ist nicht überwacht Ereignisse oder wurde bereits Müll gesammelt. Verwenden Sie die MonitorEvents() - Erweiterungsmethode, um Ereignisse zu überwachen.

Ich sehe nicht, wie MonitorEvents würde mich interessieren, wenn ich erwarten oder Thread.Sleep. Was vermisse ich? Ich bekomme, dass await verlässt die Methode und kommt zurück, während Thread.Sleep nicht.

Also, wenn es die TestMethod1 während der Wartezeit verlässt, trifft es eine Verfügung über ein Objekt, das FluentAsserts verwendet, um die Eigenschaften zu verfolgen? Kann es? Sollte es?

Antwort

2

Ja, die Dinge sind wie Sie sagten: await pausiert die Ausführung der aktuellen Methode und erstellen Sie eine Zustandsmaschine, um zurück zu der Methode, nachdem die Delay getan wird. Aber der Aufrufer (eine UnitTest-Engine) erwartet nicht, dass Ihre Tests asynchron sind und beendet einfach die Ausführung, was zur Entsorgung der Objekte führt.

Stephen Cleary ein brillantes MSDN article about Unit testing and async/await keywords schrieb, sollten Sie vielleicht Ihren Code aus dem Verfahren bewegen, um die Task Rückkehr und für die ganze es in Test warten, so etwas wie diese:

async Task Testing() 
{ 
    await Task.Delay(100); 
    this.notifyCount.Should().Be(1); 
    this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count); 
} 

[TestMethod] 
public async Task TestMethod1() 
{ 
    await Testing(); 
} 

dies kann aber immer noch nicht als die Logik nach await kann ausgeführt werden, nachdem das Einwegprodukt entsorgt wurde.

0

Sieht aus wie Version 5.0.0 wird Unterstützung für Async-Tests mit Überwachung enthalten.

Diese Issue wurde ausgelöst und die work completed wartet nur auf die Dokumentation zu updated und Version 5.0.0 freigegeben werden.

Aber zur Zeit gibt es eine Pre-Release mit dem Codeänderung 5.0.0-beta2 kann aus den Vorabversionen auf NuGet

aus dem Änderungsprotokoll erhalten:

{} Brechen die alte Gewinde- Ersetzten unsichere MonitorEvents-API mit einer neuen Monitorerweiterungsmethode, die einen thread-sicheren Überwachungsbereich von zurückgibt, der Methoden wie Should() verfügbar macht.Raise() und Metadaten wie OccurredEvents und MonitoredEvents

So ist der Code mit dem aktualisierten NuGet wird wie folgt aussehen:

[TestInitialize] 
public void TestSetup() 
{ 
    this.notifyCount = 0; 
    this.simpleNotify = new SimpleNotify(); 
    this.simpleNotify.PropertyChanged += SimpleNotify_PropertyChanged; 

    Thread thread = new Thread(this.bumpCount) 
    { 
     IsBackground = true, 
     Name = @"My Background Thread", 
     Priority = ThreadPriority.Normal 
    }; 
    thread.Start(); 
} 

[TestMethod] 
public async Task TestMethod1() 
{ 
    using (var MonitoredSimpleNotify = this.simpleNotify.Monitor()) 
    { 
     await Task.Delay(100); 
     this.notifyCount.Should().Be(1); 
     MonitoredSimpleNotify.Should().RaisePropertyChangeFor(x => x.Count); // New API for Monitoring 
    } 
} 
Verwandte Themen