2010-08-02 27 views
29

Estoy empezando con TDD y podría resolver la mayoría de los problemas que he enfrentado por mi cuenta. Pero ahora estoy perdido: ¿cómo puedo verificar si los eventos se disparan? Estaba buscando algo como Assert.Raise o Assert.Fire pero no hay nada. Google no fue muy útil, la mayoría de los hits fueron sugerencias como foo.myEvent += new EventHandler(bar); Assert.NotNull(foo.myEvent); pero eso no prueba nada.Probar eventos con nunit

Gracias!

Respuesta

40

Verificando fueron disparados se puede hacer eventos mediante la suscripción de ese evento y el establecimiento de un valor booleano:

var wasCalled = false; 
foo.NyEvent += (o,e) => wasCalled = true; 

... 

Assert.IsTrue(wasCalled); 

Debido a la solicitud - sin lambdas:

var wasCalled = false; 
foo.NyEvent += delegate(o,e){ wasCalled = true;} 

... 

Assert.IsTrue(wasCalled); 
0

Puede agregar su controlador de eventos personalizado que, por ejemplo, incremente algún campo entero en la clase de caso de prueba. Y luego verifica si el campo se incrementó.

1

realidad, no he hecho lo mismo, pero tal vez podrías agregar un manejador de eventos ficticio al evento al que deseas suscribirte y hacer que actualice una variable booleana local para que luego de que se active el método, puedas verificar el estado de ese booleano para ver si se disparó el evento.

Algo así como:

bool eventFired = false; 
foo.MyEvent += (s, e) => { eventFired = true }; 

Assert.IsTrue(eventFired); 
1

@theburningmonk: A ";" Está perdido. La versión corregida es:

bool eventFired = false; 
foo.MyEvent += (s, e) => { eventFired = true; }; 
Assert.IsTrue(eventFired); 

¡Salud! ;-)

+0

buena captura! Actualizaría mi respuesta, pero no tiene mucho sentido, ya que de todos modos es un duplicado de la respuesta de Dror – theburningmonk

5

Hace poco tuve que hacer esto, y abajo es lo que se me ocurrió. La razón por la que no hice lo que dijeron las otras publicaciones es que no me gusta la idea de un estado variable y tener que restablecerlo "manualmente" entre múltiples eventos.

a continuación es el código de la ClassUnderTest con NameChanged caso de que se prueba en MyTests pruebas:

public class ClassUnderTest { 
    private string name; 
    public string Name { 
     get { return this.name; } 
     set { 
      if (value != this.name) { 
       this.name = value; 
       NameChanged(this, new PropertyChangedEventArgs("Name")); 
      } 
     } 
    } 

    public event EventHandler<PropertyChangedEventArgs> NameChanged = delegate { }; 
} 

[TestFixture] 
public class MyTests { 
    [Test] 
    public void Test_SameValue() { 
     var t = new ClassUnderTest(); 
     var e = new EventHandlerCapture<PropertyChangedEventArgs>(); 
     t.NameChanged += e.Handler; 

     Event.Assert(e, Event.IsNotRaised<PropertyChangedEventArgs>(),() => t.Name = null); 
     t.Name = "test"; 
     Event.Assert(e, Event.IsNotRaised<PropertyChangedEventArgs>(),() => t.Name = "test"); 
    } 
    [Test] 
    public void Test_DifferentValue() { 
     var t = new ClassUnderTest(); 
     var e = new EventHandlerCapture<PropertyChangedEventArgs>(); 
     t.NameChanged += e.Handler; 

     Event.Assert(e, Event.IsPropertyChanged(t, "Name"),() => t.Name = "test"); 
     Event.Assert(e, Event.IsPropertyChanged(t, "Name"),() => t.Name = null); 
    } 
} 

Las clases de soporte son a continuación. Las clases se pueden usar con cualquier EventHandler<TEventArgs> o expandirse a otros delegados. Las pruebas de eventos se pueden anidar.

/// <summary>Class to capture events</summary> 
public class EventHandlerCapture<TEventArgs> where TEventArgs : EventArgs { 
    public EventHandlerCapture() { 
     this.Reset(); 
    } 

    public object Sender { get; private set; } 
    public TEventArgs EventArgs { get; private set; } 
    public bool WasRaised { get; private set; } 

    public void Reset() { 
     this.Sender = null; 
     this.EventArgs = null; 
     this.WasRaised = false; 
    } 

    public void Handler(object sender, TEventArgs e) { 
     this.WasRaised = true; 
     this.Sender = sender; 
     this.EventArgs = e; 
    } 
} 

/// <summary>Contains things that make tests simple</summary> 
public static class Event { 
    public static void Assert<TEventArgs>(EventHandlerCapture<TEventArgs> capture, Action<EventHandlerCapture<TEventArgs>> test, Action code) where TEventArgs : EventArgs { 
     capture.Reset(); 
     code(); 
     test(capture); 
    } 
    public static Action<EventHandlerCapture<TEventArgs>> IsNotRaised<TEventArgs>() where TEventArgs : EventArgs { 
     return (EventHandlerCapture<TEventArgs> test) => { 
      NUnit.Framework.Assert.That(test.WasRaised, Is.False); 
     }; 
    } 
    public static Action<EventHandlerCapture<PropertyChangedEventArgs>> IsPropertyChanged(object sender, string name) { 
     return (EventHandlerCapture<PropertyChangedEventArgs> test) => { 
      NUnit.Framework.Assert.That(test.WasRaised, Is.True); 
      NUnit.Framework.Assert.That(test.Sender, Is.SameAs(sender)); 
      NUnit.Framework.Assert.That(test.EventArgs.PropertyName, Is.EqualTo(name)); 
     }; 
    } 
} 
8

Si conoce el evento se dispara de forma sincronizada:

bool eventRaised = false; 
Customer customer = new Customer() { Name = "Carl" }; 
customer.NameChanged += (sender, e) => { eventRaised = true; }; 

customer.Name = "Sam"; 

Assert.IsTrue(eventRaised); 

Si el evento puede ser despedido de forma asíncrona:

ManualResetEvent eventRaised = new ManualResetEvent(false); 
Customer customer = new Customer() { Name = "Carl" }; 
customer.NameChanged += (sender, e) => { eventRaised.Set(); }; 

customer.Name = "Sam"; 

Assert.IsTrue(eventRaised.WaitOne(TIMEOUT)); 

Sin embargo, algunos dicen que las pruebas comportamiento asincrónico debe ser evitado

6

Prefiero hacer de la siguiente manera:

var wait = new AutoResetEvent(false); 
foo.MeEvent += (sender, eventArgs) => { wait.Set(); }; 
Assert.IsTrue(wait.WaitOne(TimeSpan.FromSeconds(5))); 

Ventajas: Soporta escenario de múltiples hilos (si controlador se invoca en el subproceso diferente)

+0

Estoy tomando un enfoque similar, pero prefiero 'ManualResetEvent'. – Oliver

0

Usando NUnit y Moq que puede hacer la prueba de eventos más robusto.

Mock Clase usado para monitorear los activadores de evento:

public class AssertEvent { public virtual void Call(string obj) { } } 
Mock<AssertEvent> EventMock; 
AssertEvent Evt; 

instalación de activadores de eventos:

[SetUp] 
public void TestInit() { 
    EventMock = new Mock<AssertEvent>(); 
    Evt= EventMock.Object; 
} 

El uso de objetos Mock en las pruebas:

[Test] 
public void TestMethod() { 
    myObject.Event1 += (sender, args) => Evt.Call("Event1Label"); 
    myObject.Event2 += (sender, args) => Evt.Call("Event2Label"); 
    myObject.Event3 += (sender, args) => Evt.Call("Event3Label");   

    myObject.SomeEventTrigger(); 

    EventMock.Verify(m => m.Call("Event1Label"), Times.Exactly(1)); 
    EventMock.Verify(m => m.Call("Event2Label"), Times.Never()); 
    EventMock.Verify(m => m.Call("Event3Label"), Times.Between(1,3); 

}