Reputation: 3129
I am using Moq(4.2.1502.911) in my unit tests (xUnit). In a constructor, the object being constructed tries to subscribe to events of dependencies(arguments), but it does not seem to work.
The below code is an example to simulate the problem. Alarm class uses ICam interface dependency to alert when something moves.
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;
}
}
Below are unit tests.
First Test: In the constructor, Alarm object subscribes to ICam's event, but in unit test when I raise the event of the ICam mock object, Alarm's event is not raised.
[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
}
Second Test: Explicitly calls Subscribe method and the test passes.
[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
}
The problem seems to occur due to some kind of laziness at the initialization stage of mock objects but I am not sure about it.
Is there any solution or any other way to ensure event subscription?
Upvotes: 1
Views: 756
Reputation: 101052
The problem is not Moq, the problem is your code here:
_cam.VisualDetected += VisualDetected;
Here, you add all delegates from the VisualDetected
event of this
and attach them to the VisualDetected
event of _cam
.
Your problem is that at this point in time, there are no delegates attached to the VisualDetected
of this
event, so no delegates are added to _cam.VisualDetected
.
Adding a handler to alarm.VisualDetected
later does not affect _cam.VisualDetected
(and does not add that handler to it).
If you want to "forward" the event, you can simply attach a handler to _cam.VisualDetected
that calls this.VisualDetected
:
public Alarm(ICam cam)
{
_cam = cam;
_cam.VisualDetected += (s, e) =>
{
if(VisualDetected != null)
VisualDetected(this, e);
};
}
Upvotes: 1