Tho*_*mas 12 c# reflection events delegates unit-testing
我想测试设置某个属性(或更一般地,执行一些代码)会在我的对象上引发某个事件.在这方面我的问题类似于单元测试,在C#中引发一个事件,但我需要很多这些测试而且我讨厌样板.所以我正在寻找一种更通用的解决方案,使用反射.
理想情况下,我想做这样的事情:
[TestMethod]
public void TestWidth() {
MyClass myObject = new MyClass();
AssertRaisesEvent(() => { myObject.Width = 42; }, myObject, "WidthChanged");
}
Run Code Online (Sandbox Code Playgroud)
为了实现AssertRaisesEvent,我来到这里:
private void AssertRaisesEvent(Action action, object obj, string eventName)
{
EventInfo eventInfo = obj.GetType().GetEvent(eventName);
int raisedCount = 0;
Action incrementer = () => { ++raisedCount; };
Delegate handler = /* what goes here? */;
eventInfo.AddEventHandler(obj, handler);
action.Invoke();
eventInfo.RemoveEventHandler(obj, handler);
Assert.AreEqual(1, raisedCount);
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我的问题在于Delegate为此事件创建适当的类型.代理除了调用之外什么都不做incrementer.
由于C#中的所有语法糖浆,我对委托和事件如何工作的概念有点模糊.这也是我第一次涉足反思.丢失的部分是什么?
使用lambdas,您可以使用非常少的代码完成此操作.只需为事件分配一个lambda,并在处理程序中设置一个值.无需反思,您获得强烈的类型重构
[TestFixture]
public class TestClass
{
[Test]
public void TestEventRaised()
{
// arrange
var called = false;
var test = new ObjectUnderTest();
test.WidthChanged += (sender, args) => called = true;
// act
test.Width = 42;
// assert
Assert.IsTrue(called);
}
private class ObjectUnderTest
{
private int _width;
public event EventHandler WidthChanged;
public int Width
{
get { return _width; }
set
{
_width = value; OnWidthChanged();
}
}
private void OnWidthChanged()
{
var handler = WidthChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我最近写了一系列关于发布同步和异步事件的对象的单元测试事件序列的博客文章.这些帖子描述了单元测试方法和框架,并提供了完整的源代码和测试.
我描述了一个"事件监视器"的实现,它允许编写事件排序单元测试更清晰地编写,即摆脱所有凌乱的样板代码.
使用我的文章中描述的事件监视器,可以像这样编写测试:
var publisher = new AsyncEventPublisher();
Action test = () =>
{
publisher.RaiseA();
publisher.RaiseB();
publisher.RaiseC();
};
var expectedSequence = new[] { "EventA", "EventB", "EventC" };
EventMonitor.Assert(publisher, test, expectedSequence);
Run Code Online (Sandbox Code Playgroud)
或者对于实现INotifyPropertyChanged的类型:
var publisher = new PropertyChangedEventPublisher();
Action test = () =>
{
publisher.X = 1;
publisher.Y = 2;
};
var expectedSequence = new[] { "X", "Y" };
EventMonitor.Assert(publisher, test, expectedSequence);
Run Code Online (Sandbox Code Playgroud)
对于原始问题中的情况:
MyClass myObject = new MyClass();
EventMonitor.Assert(myObject, () => { myObject.Width = 42; }, "Width");
Run Code Online (Sandbox Code Playgroud)
EventMonitor执行所有繁重操作并将运行测试(操作)并断言事件是按预期顺序(expectedSequence)引发的.它还会在测试失败时输出很好的诊断消息.反射和IL用于获取动态事件订阅的工作,但这都是很好的封装,所以只需要像上面这样的代码来编写事件测试.
帖子中有很多细节描述了问题和方法,以及源代码:
http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part-1/
| 归档时间: |
|
| 查看次数: |
6613 次 |
| 最近记录: |