I'm using Moq, xUnit and Prism 4. My unit test's objective is to fire an event and confirm that a property has changed in my view model to match the value from the event. This test, by the way, fails (Expected:5, Actual:0):
// Version One
[Fact]
public void Should_set_DayCount_on_DayCountChangedEvent()
{
var eaMock = new Mock<IEventAggregator>();
eaMock.SetupCurriculumEvents(); // see below
var vm = new CurriculumItemViewModel(eaMock.Object, _systemStatus.Object);
vm.Load(_newItem);
var dayCount = 5;
eaMock.Object.GetEvent<DayCountChangedNotification>().Publish(dayCount);
Assert.Equal(dayCount, _vm.DayCount);
}
I got tired of setting up my event aggregator mock everywhere, so I created an extension method to do the dirty work for me:
public static void SetupCurriculumEvents(this Mock<IEventAggregator> eaMock)
{
eaMock.Setup(ea => ea.GetEvent<DayCountChangedNotification>())
.Returns(new DayCountChangedNotification());
// lots of other "notification" events here as well
}
Then I realized that I'm creating a new event every time it's retrieved from the mock event aggregator, so the Subscribe()
on one instance (in the VM) isn't on the same instance as the Publish(dayCount)
in my test.
Well, methinks, let's just always use the same object (by overwriting the extension method's Setup()
for this event) and we're gonna be good:
// Version Two
[Fact]
public void Should_set_DayCount_on_DayCountChangedEvent()
{
var dayCountChangedEvent = new DayCountChangedNotification();
var eaMock = new Mock<IEventAggregator>();
eaMock.SetupCurriculumEvents(); // still need this for all the other events
// overwrite the setup from the extension method
eaMock.Setup(ea => ea.GetEvent<DayCountChangedNotification>())
.Returns(dayCountChangedEvent);
var vm = new CurriculumItemViewModel(eaMock.Object, _systemStatus.Object);
vm.Load(_newItem);
var dayCount = 5;
dayCountChangedEvent.Publish(dayCount);
Assert.Equal(dayCount, _vm.DayCount);
}
... which also fails spectacularly.
For some reason, I decided to try refactoring the extension method, (and reverted the unit test back to Version One):
public static class MockingExtensions
{
private static DayCountChangedNotification DayNotification = new DayCountChangedNotification();
public static void SetupCurriculumEvents(this Mock<IEventAggregator> eaMock)
{
eaMock.Setup(ea => ea.GetEvent<DayCountChangedNotification>())
.Returns(DayNotification);
// etc...
}
}
... which to my mind, is basically the same thing - I'm always returning the same instance of the Event.
Here's the kicker: this test passes.
That's great and everything, but I don't understand why it passes - and if I don't understand why it's passing, then I don't really know if it's right or not.
Accepted answer needs to explain two things:
I tried to reproduce your problem and created the code below. All three tests succeeded so i think something is missing in the question. It is important to know that moqObject.Setup(...).Return(true) is not the same as moqObject.Setup(...).Return(() => true). See for more info here
namespace Test
{
using Microsoft.Practices.Prism.Events;
using Moq;
using System;
using Xunit;
// Moq 4.2.1510.2205
// Prism 4.0.0.0
// xunit 2.1.0
public class CurriculumItemViewModel
{
public CurriculumItemViewModel(IEventAggregator agg)
{
agg.GetEvent<DayCountChangedNotification>().Subscribe((int? x) => {
DayCount = x.Value;
});
}
public int DayCount { get; set; }
}
public class DayCountChangedNotification : CompositePresentationEvent<int?>
{
public DateTime Created = DateTime.Now;
}
public static class Extensions
{
private static DayCountChangedNotification DayNotification = new DayCountChangedNotification();
public static void SetupCurriculumEventsV1(this Mock<IEventAggregator> eaMock)
{
eaMock.Setup(ea => ea.GetEvent<DayCountChangedNotification>())
.Returns(new DayCountChangedNotification());
}
public static void SetupCurriculumEventsV2(this Mock<IEventAggregator> eaMock)
{
eaMock.Setup(ea => ea.GetEvent<DayCountChangedNotification>())
.Returns(DayNotification);
}
}
public class TestClass
{
[Fact]
public void ShouldSetDayCountOnDayCountChangedEvent1a()
{
// Arrange
var dayCount = 5;
var eaMock = new Mock<IEventAggregator>();
eaMock.SetupCurriculumEventsV1();
var vm = new CurriculumItemViewModel(eaMock.Object);
var notification = eaMock.Object.GetEvent<DayCountChangedNotification>();
var notification2 = eaMock.Object.GetEvent<DayCountChangedNotification>();
// Act
notification.Publish(dayCount);
// Assert
Assert.Equal(dayCount, vm.DayCount);
}
[Fact]
public void ShouldSetDayCountOnDayCountChangedEvent2()
{
// Arrange
var dayCount = 5;
var eaMock = new Mock<IEventAggregator>();
eaMock.SetupCurriculumEventsV1();
// This will override the setup done by SetupCurriculumEventsV1
var notification = new DayCountChangedNotification();
eaMock.Setup(ea => ea.GetEvent<DayCountChangedNotification>())
.Returns(notification);
var vm = new CurriculumItemViewModel(eaMock.Object);
// Act
notification.Publish(dayCount);
// Assert
Assert.Equal(dayCount, vm.DayCount);
}
[Fact]
public void ShouldSetDayCountOnDayCountChangedEvent1b()
{
// Arrange
var dayCount = 5;
var eaMock = new Mock<IEventAggregator>();
eaMock.SetupCurriculumEventsV2();
var vm = new CurriculumItemViewModel(eaMock.Object);
var notification = eaMock.Object.GetEvent<DayCountChangedNotification>();
// Act
notification.Publish(dayCount);
// Assert
Assert.Equal(dayCount, vm.DayCount);
}
}
}