2010-08-02 3 views
29

Я только начинаю с TDD и могу решить большинство проблем, с которыми я столкнулся сам по себе. Но теперь я потерян: как я могу проверить, запущены ли события? Я искал что-то вроде Assert.Raise или Assert.Fire, но ничего. Google был не очень полезен, большинство хитов были предложениями, такими как foo.myEvent += new EventHandler(bar); Assert.NotNull(foo.myEvent);, но это ничего не доказывает.Тестовые события с nunit

Спасибо!

ответ

40

Проверка события были уволены может быть сделано путем подписки на это событие и установку логического значения:

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

... 

Assert.IsTrue(wasCalled); 

Благодаря запросу - без лямбды:

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

... 

Assert.IsTrue(wasCalled); 
0

Вы можете добавить свой собственный обработчик событий, который, например, увеличивает некоторое целочисленное поле в классе тестового случая. И затем проверьте, увеличилось ли поле.

1

Не действительно сделал это сам, но, возможно, вы могли бы добавить обработчик фиктивного события к событию, на которое хотите подписаться, и обновить локальную логическую переменную, чтобы после запуска метода вы могли проверить состояние этого логического объекта, чтобы узнать, было ли событие запущено?

Что-то вроде:

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

Assert.IsTrue(eventFired); 
1

@theburningmonk: A ";" пропал, отсутствует. Исправленная версия:

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

Cheers! ;-)

+0

хороший улов! Я бы обновил свой ответ, но не так много, потому что это действительно дубликат ответа Дрора в любом случае. – theburningmonk

5

Мне недавно пришлось это сделать, а ниже - то, с чем я столкнулся. Причина, по которой я не делал то, что говорили другие сообщения, мне не нравится идея состояния сохранения переменных и необходимость ее «вручную» перезаписывать между несколькими событиями.

Ниже приведен код ClassUnderTest с NameChanged события, испытанной в MyTests тестах:

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

Опорные классы ниже. Классы могут использоваться с любыми EventHandler<TEventArgs> или расширены для других делегатов. Тесты событий могут быть вложенными.

/// <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

Если вы знаете, что событие будет срабатывать синхронно:

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

customer.Name = "Sam"; 

Assert.IsTrue(eventRaised); 

Если событие может быть асинхронно:

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

Однако, некоторые говорят, что тестирование асинхронного поведения должны избегать.

6

Я предпочитаю делать следующим образом:

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

Преимущества: Поддержка многопоточности сценарий (если обработчик вызывается в другом потоке)

+0

Я использую подобный подход, но предпочитаю 'ManualResetEvent'. – Oliver

0

Использование NUnit и Moq вы можете сделать более надежное тестирование событий.

Mock Класс используется для мониторинга триггеры событий:

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

Установка для события Триггеры:

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

Использование фиктивных объектов в тестах:

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

} 
Смежные вопросы