为什么我的EventAggregator订阅处理此事件?

时间:2016-04-24 02:46:19

标签: unit-testing mvvm moq prism autofac

我有一个Autofac DI Container定义如下:

public class Bootstrapper
{
    public IContainer BootStrap()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<ItemViewModel>().AsSelf();
        builder.RegisterType<EventAggregator>()
            .As<IEventAggregator>()
            .SingleInstance(); 
     }
}

我定义了一个单元测试来测试删除是否从集合中删除了已删除的项目:

[Fact]
public void Should_remove_item_from_collection_when_item_is_deleted()
{
  const int deletedId = 42;
  // adds three items to the collection
  _openItemEditViewEvent.Publish(deletedId);
  _openItemEditViewEvent.Publish(8);
  _openItemEditViewEvent.Publish(9);

// I've tried this:
     _eventAggregatorMock.Object.GetEvent<ItemDeletedEvent>().Publish(42);
// and alternatively, this (not at the same time):
     _itemDeletedEventMock.Object.Publish(42);  

  Assert.Equal(2,_vm.ItemEditViewModels.Count); // always fails
  Assert.False(_vm.ItemEditViewModels
     .Select(vm => vm.Item.Id).Contains(42), "Wrong item deleted");
}

Unit Test的构造函数初始化并将EventAggregator分配给视图模型:

_eventAggregatorMock = new Mock<IEventAggregator>();

_itemDeletedEventMock = new Mock<ItemDeletedEvent>();
_eventAggregatorMock.Setup(ea => ea.GetEvent<ItemDeletedEvent>())
    .Returns(_itemDeletedEventMock.Object);

_vm = new ItemViewModel(_eventAggregatorMock.Object, */ ... /*);

在我的实际视图模型中,我订阅了该活动:

public ItemViewModel(IEventAggregator ea, /* ... */)
{
   _eventAggregator.GetEvent<ItemDeletedEvent>()
      .Subscribe(OnItemDeleted, true);  
}

我们从未在这里遇到过断点:

public void OnItemDeleted()
{
  // never happens
}

对于我的生活,我无法弄清楚我做错了什么 - 我忽略了一些事情......我是否必须设置活动的发布活动模拟?我应该使用真正的ItemDeletedEvent实例而不是模拟吗?任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:3)

=&GT;嗨斯科特,

使用EventAggregator时要测试2个ViewModel场景:

  1. 您想测试您的ViewModel是否正在发布活动
  2. 您希望测试ViewModel在发布事件时执行的操作。因此,ViewModel必须订阅该事件才能执行某些操作
  3. (注意:以下几行适用于PRISM的EventAggregator,我猜你正在使用它。对于其他EventAggregators,它可能会有所不同)

    对于第一个场景,您必须为该事件创建一个模拟。然后,您可以在该模拟实例上验证已调用事件的Publish方法。

    对于第二个场景,即您在问题中的场景,您必须在测试中使用真实事件。为什么呢?

    • 当您在事件模拟上调用Publish方法时,该Publish方法不会调用该事件的订阅者,因为Subscribe-method背后没有逻辑。当然,您可以设置这两种方法并在模拟中实现发布/订阅逻辑。但是没有理由这样做,只需使用真实的事件
    • 当您使用真实事件时,Publish方法将调用所有订阅者。这正是您在测试中所需要的。

    它应该是这样的:

    _itemDeletedEvent = new ItemDeletedEvent();
    _eventAggregatorMock.Setup(ea => ea.GetEvent<ItemDeletedEvent>())
      .Returns(_itemDeletedEvent);
    

    现在,您的ViewModel将从EventAggregator获取此itemDeletedEvent-instance。在您的测试中,您可以在此itemDeletedEvent-instance上调用Publish-method,它将起作用。

    有关这方面的更多信息,请参阅我的关于WPF和测试驱动开发的Pluralsight课程:http://www.pluralsight.com/courses/wpf-mvvm-test-driven-development-viewmodels

    托马斯 http://www.thomasclaudiushuber.com