NSubstitute完全手册(十四)引发事件
有时,引发被替代的类型中定义的事件时非常必要的。例如下面这个例子:
1 public interface IEngine 2 { 3 event EventHandler Idling; 4 event EventHandler<LowFuelWarningEventArgs> LowFuelWarning; 5 event Action<int> RevvedAt; 6 } 7 8 public class LowFuelWarningEventArgs : EventArgs 9 { 10 public int PercentLeft { get; private set; } 11 public LowFuelWarningEventArgs(int percentLeft) 12 { 13 PercentLeft = percentLeft; 14 } 15 }
在 .NET 中,事件是非常有意思的功能,因为你不能像操作其他成员似地进行传递。相反地,你只能添加或移除事件处理器,NSubstitute 正是使用了这个添加事件处理器的语法来引发事件。
1 [TestMethod] 2 public void Test_RaisingEvents_RaiseEvent() 3 { 4 var engine = Substitute.For<IEngine>(); 5 6 var wasCalled = false; 7 engine.Idling += (sender, args) => wasCalled = true; 8 9 // 告诉替代实例引发异常,并携带指定的sender和事件参数 10 engine.Idling += Raise.EventWith(new object(), new EventArgs()); 11 12 Assert.IsTrue(wasCalled); 13 }
在上面的例子中,我们不能真实地了解到我们所引发事件的发送者和参数,仅是知道它被调用了。在这种条件下,NSubstitute 通过为我们的事件处理器创建所需的参数,来使该操作更便捷些。
1 [TestMethod] 2 public void Test_RaisingEvents_RaiseEventButNoMindSenderAndArgs() 3 { 4 var engine = Substitute.For<IEngine>(); 5 6 var wasCalled = false; 7 engine.Idling += (sender, args) => wasCalled = true; 8 9 engine.Idling += Raise.Event(); 10 Assert.IsTrue(wasCalled); 11 }
当参数没有默认构造函数时引发事件
NSubstitute 不总是能够创建事件参数。如果事件的参数没有默认的构造函数,你可能不得不使用 Raise.EventWith<TEventArgs>(...) 来创建事件参数,例如下面例子中的 LowFuelWarning 事件。如果没有提供事件的发送者,则 NSubstitute 会创建。
1 [TestMethod] 2 public void Test_RaisingEvents_ArgsDoNotHaveDefaultCtor() 3 { 4 var engine = Substitute.For<IEngine>(); 5 6 int numberOfEvents = 0; 7 engine.LowFuelWarning += (sender, args) => numberOfEvents++; 8 9 // 发送事件,并携带指定的事件参数,未指定发送者 10 engine.LowFuelWarning += Raise.EventWith(new LowFuelWarningEventArgs(10)); 11 // 发送事件,并携带指定的事件参数,并指定发送者 12 engine.LowFuelWarning += Raise.EventWith(new object(), new LowFuelWarningEventArgs(10)); 13 14 Assert.AreEqual(2, numberOfEvents); 15 }
引发Delegate事件
有时事件会通过委托来声明,而没有继承自 EventHandler<> 或 EventHandler。这种事件可以通过使用 Raise.Event<TypeOfEventHandlerDelegate>(arguments)来引发。NSubstitute 会尝试和猜测该委托所需的参数,但如果没成功,NSubstitute 会告诉你具体需要提供哪个参数。
下面这个示例演示了引发 INotifyPropertyChanged 事件,该事件使用 PropertyChangedEventHandler 委托并需要2个参数。
1 [TestMethod] 2 public void Test_RaisingEvents_RaisingDelegateEvents() 3 { 4 var sub = Substitute.For<INotifyPropertyChanged>(); 5 bool wasCalled = false; 6 7 sub.PropertyChanged += (sender, args) => wasCalled = true; 8 9 sub.PropertyChanged += Raise.Event<PropertyChangedEventHandler>( 10 this, new PropertyChangedEventArgs("test")); 11 12 Assert.IsTrue(wasCalled); 13 }
引发Action事件
在 IEngine 示例中,RevvedAt 事件被声明为 Action<int>。这是委托事件的另外一种形式,我们可以使用 Raise.Event<Action<int>>() 来引发该事件。
1 [TestMethod] 2 public void Test_RaisingEvents_RaisingActionEvents() 3 { 4 var engine = Substitute.For<IEngine>(); 5 6 int revvedAt = 0; 7 engine.RevvedAt += rpm => revvedAt = rpm; 8 9 engine.RevvedAt += Raise.Event<Action<int>>(123); 10 11 Assert.AreEqual(123, revvedAt); 12 }