我不明白这些单元测试的区别

I Don't Understand The Difference In These Unit Tests

我正在使用 Moq、xUnit 和 Prism 4。我的单元测试 objective 是触发一个事件并确认 属性 在我的视图模型中已更改以匹配来自事件。顺便说一句,这个测试失败了(预期: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);
}

我厌倦了到处设置我的事件聚合器模拟,所以我创建了一个扩展方法来为我做脏活:

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
}

然后我意识到每次从模拟事件聚合器检索到它时我都会创建一个新事件,因此一个实例(在 VM 中)上的 Subscribe()Publish(dayCount) 在我的测试中。

好吧,我想,让我们始终使用相同的对象(通过为此事件覆盖扩展方法的 Setup()),我们会很好:

// 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);
}

...这也失败了。

出于某种原因,我决定尝试重构扩展方法,(并将单元测试恢复为版本一):

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...
    }
}

...在我看来,这基本上是同一件事——我总是返回相同的事件实例。

关键是:这个测试通过了

一切都很好,但我不明白为什么 它通过了 - 如果我不明白它为什么通过,那么我真的不知道它是否通过了对不对。

接受的答案需要解释两[=4​​4=]件事:

  1. 为什么重构后的静态实例扩展方法通过了?
  2. 为什么第二版没有通过?

我试图重现您的问题并创建了以下代码。所有三个测试都成功了,所以我认为问题中缺少一些东西。重要的是要知道 moqObject.Setup(...).Return(true) 与 moqObject.Setup(...).Return(() = > 真)。有关详细信息,请参阅 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);            
        }
    }
}