【问题标题】:I Don't Understand The Difference In These Unit Tests我不明白这些单元测试的区别
【发布时间】:2016-05-06 19:44:54
【问题描述】:

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

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

这是关键:这个测试通过了

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

接受的答案需要解释两件事

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

【问题讨论】:

  • 声明静态类字段与局部变量不同。您是仅运行此测试还是在与其他测试一起运行时运行此测试? DayCountChangedNotification 的实现是什么?
  • DayCountChangedNotification 只是一个空类,它继承自 Prism 的 CompositePresentationEvent&lt;int?&gt; 类。该测试既可以单独运行,也可以与其他测试联合运行 - 结果是一致的。

标签: unit-testing moq xunit prism-4


【解决方案1】:

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

【讨论】:

  • 很好的答案 - 给我几天时间来研究一下,我会告诉你的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多