Moq 不订阅构造函数中的事件

Moq does not subscribe to events in constructor

我在单元测试 (xUnit) 中使用 Moq(4.2.1502.911)。在构造函数中,被构造的对象试图订阅依赖事件(参数),但它似乎不起作用。

下面的代码是模拟问题的例子。 Alarm class 使用 ICam 接口依赖性在有物体移动时发出警报。

public interface ICam
{
    event EventHandler VisualDetected;
}

public class Alarm : ICam
{
    private ICam _cam;

    public Alarm(ICam cam)
    {
        _cam = cam;

        // Subscribe to forward events, DOES NOT WORK
        _cam.VisualDetected += VisualDetected;
    }

    public event EventHandler VisualDetected;

    // If I call this method explicitly, test succeeds
    public void Subscribe()
    {
        // Subscribe to forward events outside the constructor
        _cam.VisualDetected += VisualDetected;
    }
}

下面是单元测试。

第一次测试:在构造函数中,Alarm对象订阅了ICam的事件,但是在单元测试中,当我引发ICam模拟对象的事件时,Alarm的事件未引发。

    [Fact]
    public void Alarm_SubscribesInCtor()
    {
        var cam = new Mock<ICam>();
        var alarm = new Alarm(cam.Object);
        var raised = false;
        alarm.VisualDetected += (o, e) => raised = true;

        cam.Raise(c => c.VisualDetected += null, new EventArgs());

        Assert.True(raised); // FAILS
    }

第二个测试:显式调用 Subscribe 方法,测试通过。

    [Fact]
    public void Alarm_SubscribesOutsideCtor()
    {
        var cam = new Mock<ICam>();
        var alarm = new Alarm(cam.Object);
        var raised = false;
        alarm.VisualDetected += (o, e) => raised = true;
        alarm.Subscribe();

        cam.Raise(c => c.VisualDetected += null, new EventArgs());

        Assert.True(raised); // SUCCEEDS
    }

这个问题似乎是由于模拟对象初始化阶段的某种惰性造成的,但我不确定。

是否有任何解决方案或任何其他方式来确保事件订阅?

问题不是最小起订量,问题是您的代码:

_cam.VisualDetected += VisualDetected;

在这里,您添加来自 thisVisualDetected 事件的所有代表,并将它们附加到 _camVisualDetected 事件。

您的问题是此时 this 事件的 VisualDetected 没有代表,因此没有代表被添加到 _cam.VisualDetected.

稍后向 alarm.VisualDetected 添加处理程序不会影响 _cam.VisualDetected(并且不会向其添加该处理程序)。

如果你想 "forward" 事件,你可以简单地将一个处理程序附加到 _cam.VisualDetected 调用 this.VisualDetected:

public Alarm(ICam cam)
{
    _cam = cam;

    _cam.VisualDetected += (s, e) =>  
    {
        if(VisualDetected != null) 
            VisualDetected(this, e);
    };
}