2

我正在尝试使用忽略事件的 JustMock 编写单元测试。

不想测试 Event 本身,因为它调用了各种需要大量工作来模拟的内部函数。

下面是一些示例代码:

public class Sample
{
    public delegate void OneParameterFunction(int someVar);
    public event OneParameterFunction TheEvent;               

    public Sample()
    {            
        TheEvent += CallMe;
    }

    public void CallMe(int someVar)
    {
        Debug.WriteLine("CallMe was fired with parameter: " + someVar);
    }

    public void FireEvent()
    {
        // do stuff, business logic here...
        if (TheEvent != null)
            TheEvent(3);
    }
}

这是我很想写的测试但不能:

[TestClass]
class EventMocking
{
    [TestMethod]
    public void DoNothingOnEvent()
    {
        var s = new Sample();

        Mock.Arrange(() => s.TheEvent(Arg.AnyInt))            
            .DoNothing();

        Mock.Arrange(() => s.CallMe(Arg.AnyInt))
            .OccursNever();

        s.FireEvent();

        Mock.Assert(() => s.CallMe(Arg.AnyInt));
    }
}

但我收到以下编译器错误:

Error   1   The event 'Sample.TheEvent' can only appear on the left hand side of += or -= (except when used from within the type 'Sample')  C:\BizObjectTests\EventMocking.cs

有人对如何阻止事件传播有任何建议吗?出于多种原因,我也不想这样做Mock.Create<T>,一个是我再次需要设置更多的测试数据/对象。

4

1 回答 1

2

模拟委托调用本身是不可能的,因为它是由 JIT 在内部实现的。

您有几个备选方案。如果您在专用于该目的的方法中引发事件(如在您的示例中),那么您可以简单地模拟该方法。因此,在您的示例中,变为:

Mock.Arrange(() => s.FireEvent()).DoNothing();

如果这不可能,那么您可以模拟将处理程序添加到事件的方法(调用 Sample += ... 时调用的方法)。你可以这样做:

var mock = Mock.Create<Sample>(); // we need this just for the following arrangement
Mock.ArrangeSet(() => mock.TheEvent += null).IgnoreInstance().IgnoreArguments().DoNothing();

var real = new Sample(); // TheEvent += CallMe will now do nothing
real.FireEvent(); // TheEvent is empty

最后,作为第三种选择,您可以在某个时刻使用反射从事件中删除所有处理程序,您知道该事件即将被触发,或者没有其他人将附加到它:

new PrivateAccessor(real).SetField("TheEvent", null);
real.FireEvent(); // TheEvent is null right now

警告:最后一个选项取决于编译器的实现。它适用于 C# 代码中的事件声明,但不适用于 VB 事件。

于 2014-01-22T09:11:07.477 回答