2017-04-22 2 views
0

Hallo Ich habe ein Problem beim Testen eines Ereignisses mit NUnit. Ich bin mir nicht einmal sicher, ob das ein Einheits- oder Funktionstest sein sollte. Lassen Sie mich Ihnen die Beispielklasse zeigen erste (ich versuche OnValueInjected Ereignis zu testen):Wie kann ein Ereignis während des Komponententests abgefangen werden?

public class Foo 
{ 
    private IBar CurrentBar { get; set; } 
    public event EventHandler<MoveEventArgs> OnValueInjected; 

    public Foo() 
    { 
     StartFoo(); 
    }  

    private async void StartFoo() 
    { 
     await Task.Factory.StartNew(() => 
     { 
      while (State != FooState.Finished) 
      { 
       IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value 

       OnValueInjected?.Invoke(this, new ResultEventArgs(result)); 

       // .. rest of the loop 
      } 
     }); 
    } 

    public void InjectValue(int a, int b) 
    { 
     CurrentBar.Inject(a,b); 
    } 
} 

Also, im Grunde, was ich versuche zu dem Ereignis abonnieren zu tun ist, rufen InjectValue und prüfen, ob die Veranstaltung hieß. Wie folgt aus:

[Test] 
    public void FooOnValueInjectedTest() 
    { 
     bool OnValueInjectedWasRasied = false; 

     IFoo foo = new Foo(); 
     foo.OnValueInjected += (s, e) => OnValueInjectedWasRasied = true; 

     foo.InjectValue(0,0); 

     Assert.AreEqual(true, OnValueInjectedWasRasied); 
    } 

Ziemlich einfach, aber es sieht aus wie InjectValue zu langsam ist. Der Test ist fehlgeschlagen..Ich denke es ist zu langsam, denn wenn ich Thread.Sleep zwischen InjectValue und Assert hinzufüge funktioniert.

 foo.InjectValue(0,0); 
     Thread.Sleep(1000); 
     Assert.AreEqual(true, OnValueInjectedWasRasied); 

Gibt es eine bessere Möglichkeit, ein solches Ereignis zu testen? Dank

fixierte ich meine Klasse, so ist es so, dass jetzt:

public class Foo 
{ 
    private AutoResetEvent AutoReset { get; } 

    private IBar CurrentBar { get; set; } 
    public event EventHandler<MoveEventArgs> OnValueInjected; 

    public Foo() 
    { 
     AutoReset = new AutoResetEvent(false); 
     StartFoo(); 
    } 

    private async void StartFoo() 
    { 
     await Task.Factory.StartNew(() => 
     { 
      while (State != FooState.Finished) 
      { 
       IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value 

       OnValueInjected?.Invoke(this, new ResultEventArgs(result)); 

       AutoReset.Set(); 

       // .. rest of the loop 
      } 
     }); 
    } 

    public void InjectValue(int a, int b) 
    { 
     if (CurrentBar.Inject(a,b)) 
     { 
      AutoReset.WaitOne(); 
     } 
    } 
} 
+2

Nun, wollen Sie * * Ihre 'InjectValue' zurückzukehren, bevor die Ereignishandler auf jeden Fall angesprochen wurden? Sie sehen im Grunde das natürliche Ergebnis von Asynchronität ... es geht nicht wirklich um Ereignisse, sondern um asynchrone Ausführung. –

+0

@JonSkeet hmm .. technisch, sagst du, dass "InjectValue" blockiert werden konnte, bis 'OnValueInjected' ausgelöst wurde? Ich habe nicht wirklich darüber nachgedacht .. –

+0

Wenn Sie * dieses Verhalten * wollten, könnten Sie es sicherstellen, ja. Ob Sie es tun oder nicht, ist eine andere Sache. –

Antwort

0

Ich glaube, das Problem der asynchronen Berufung. Wann immer Sie eine asynchrone Methode in einem NUnit-Test haben, wird nicht darauf gewartet, dass sie ausgeführt wird, da niemand darauf wartet, dass sie ausgeführt wird, und das Ergebnis zurückgibt. Stattdessen müssen Sie ein .Wait auf die asynchrone Methode anwenden, um zu erzwingen, dass der Test auf die Ausführung wartet.

Ich habe diesen Code nicht in einem Code-Editor geschrieben, also ist es vielleicht nicht perfekt, aber das ist die Grundidee.

public class Foo 
{ 
    private AutoResetEvent AutoReset { get; } 

    private IBar CurrentBar { get; set; } 
    public event EventHandler<MoveEventArgs> OnValueInjected; 

    public Foo() 
    { 
     AutoReset = new AutoResetEvent(false); 
     StartFoo(); 
    } 

    private async void StartFoo() 
    { 
     await Task.Factory.StartNew(() => 
     { 
      while (State != FooState.Finished) 
      { 
       IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value 

       OnValueInjected?.Invoke(this, new ResultEventArgs(result)); 

       AutoReset.Set(); 

       // .. rest of the loop 
      } 
     }); 
    } 

    public async void InjectValue(int a, int b) 
    { 
     if (CurrentBar.Inject(a,b)) 
     { 
      AutoReset.WaitOne(); 
     } 
    } 
} 

Dann in Ihrer Testmethode in dem ACT Du ein .Wait

[Test] 
public void FooOnValueInjectedTest() 
{ 
    // Arrange 
    bool OnValueInjectedWasRasied = false; 

    IFoo foo = new Foo(); 
    foo.OnValueInjected += (s, e) => OnValueInjectedWasRasied = true; 

    // Act 
    foo.InjectValue(0,0).Wait(); 

    // Assert 
    Assert.AreEqual(true, OnValueInjectedWasRasied); 
} 
+0

Danke für einen Vorschlag, aber 'foo.InjectValue (0,0);' Funktion ist nicht zu erwarten. Muss nicht sein. Es sollte einen Wert in die Sperrsammlung einfügen. Nachdem die Sammlung gefüllt ist, wird sie veröffentlicht. Es ist ein sehr reales Szenario - also habe ich mich gefragt, wie ich es testen soll (ohne den Code vonc zu ändern). Soll ich es testen? –

+0

Ich muss die Methode missverstanden haben und dachte, dass die Inject-Methode eine andere asynchrone Methode in ihr aufruft. Mein Fehler. Wie für sollten Sie es testen? Das hängt von der Antwort auf die Frage ab: "Was versuchst du zu testen?" Es scheint nicht, dass dort viel zu testen ist. – AzzamAziz

Verwandte Themen