为什么我的 EventAggregator 订阅没有处理这个事件?
Why isn't my EventAggregator Subscription Handling This Event?
我有一个 Autofac DI 容器定义如下:
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");
}
单元测试的构造函数初始化并将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
}
对于我的生活,我无法弄清楚我做错了什么 - 我忽略了一些东西......我是否必须在 Mock 中设置事件的发布事件?我应该使用真正的 ItemDeletedEvent 实例而不是 Mock 实例吗?任何帮助将不胜感激。
=> 嗨,斯科特,
在使用 EventAggregator 时,您想要测试 2 个 ViewModel 场景:
- 您想测试您的 ViewModel 是否正在发布事件
- 您想测试您的 ViewModel 在发布事件时是否执行某些操作。所以 ViewModel 必须订阅该事件才能做某事
(注意:以下几行适用于 PRISM 的 EventAggregator,我猜这就是您正在使用的那个。对于其他 EventAggregator,它可能不同)
对于第一种情况,您必须为事件创建一个模拟。然后您可以在该模拟实例上验证事件的发布方法已被调用。
对于第二种情况,也就是你问题中的情况,你必须在测试中使用真实事件。为什么?
- 当您在事件模拟上调用发布方法时,该发布方法不会调用该事件的订阅者,因为订阅方法背后没有逻辑。当然,您可以设置这两种方法并在模拟中实现 publish/subscribe-logic。但是没有理由这样做,就用真正的Event
- 当您使用真实事件时,发布方法将调用所有订阅者。这正是您在测试中需要的。
它应该是这样的:
_itemDeletedEvent = new ItemDeletedEvent();
_eventAggregatorMock.Setup(ea => ea.GetEvent<ItemDeletedEvent>())
.Returns(_itemDeletedEvent);
现在您的 ViewModel 将从 EventAggregator 获取这个 itemDeletedEvent 实例。在您的测试中,您在此 itemDeletedEvent 实例上调用 Publish 方法,它将起作用。
我的关于 WPF 和测试驱动开发的 Pluralsight 课程对此进行了详细说明:http://www.pluralsight.com/courses/wpf-mvvm-test-driven-development-viewmodels
我有一个 Autofac DI 容器定义如下:
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");
}
单元测试的构造函数初始化并将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
}
对于我的生活,我无法弄清楚我做错了什么 - 我忽略了一些东西......我是否必须在 Mock 中设置事件的发布事件?我应该使用真正的 ItemDeletedEvent 实例而不是 Mock 实例吗?任何帮助将不胜感激。
=> 嗨,斯科特,
在使用 EventAggregator 时,您想要测试 2 个 ViewModel 场景:
- 您想测试您的 ViewModel 是否正在发布事件
- 您想测试您的 ViewModel 在发布事件时是否执行某些操作。所以 ViewModel 必须订阅该事件才能做某事
(注意:以下几行适用于 PRISM 的 EventAggregator,我猜这就是您正在使用的那个。对于其他 EventAggregator,它可能不同)
对于第一种情况,您必须为事件创建一个模拟。然后您可以在该模拟实例上验证事件的发布方法已被调用。
对于第二种情况,也就是你问题中的情况,你必须在测试中使用真实事件。为什么?
- 当您在事件模拟上调用发布方法时,该发布方法不会调用该事件的订阅者,因为订阅方法背后没有逻辑。当然,您可以设置这两种方法并在模拟中实现 publish/subscribe-logic。但是没有理由这样做,就用真正的Event
- 当您使用真实事件时,发布方法将调用所有订阅者。这正是您在测试中需要的。
它应该是这样的:
_itemDeletedEvent = new ItemDeletedEvent();
_eventAggregatorMock.Setup(ea => ea.GetEvent<ItemDeletedEvent>())
.Returns(_itemDeletedEvent);
现在您的 ViewModel 将从 EventAggregator 获取这个 itemDeletedEvent 实例。在您的测试中,您在此 itemDeletedEvent 实例上调用 Publish 方法,它将起作用。
我的关于 WPF 和测试驱动开发的 Pluralsight 课程对此进行了详细说明:http://www.pluralsight.com/courses/wpf-mvvm-test-driven-development-viewmodels