使用Moq回调实现Prism IEventAggregator辅助类的问题排查
解决Prism IEventAggregator模拟辅助类回调为null的问题
问题根源
你遇到的问题核心是辅助类中实例引用或回调变量的作用域匹配错误:测试类中直接编写代码时,Mock实例、事件实例和回调变量处于同一作用域,引用完全一致;但放到辅助类后,要么是辅助类没有正确维护实例引用(比如每次调用方法都生成新实例),要么是回调变量的生命周期未正确暴露给测试类,导致始终捕获不到有效回调。
修复方案
1. 重构辅助类,维护实例引用
辅助类需要统一管理IEventAggregator Mock和对应的PubSubEvent实例,避免每次方法调用生成新实例:
public class EventAggregatorMockHelper { private readonly Mock<IEventAggregator> _eventAggregatorMock; private readonly Dictionary<Type, object> _registeredEvents = new Dictionary<Type, object>(); public EventAggregatorMockHelper() { _eventAggregatorMock = new Mock<IEventAggregator>(); // 配置GetEvent方法返回我们维护的事件实例 _eventAggregatorMock.Setup(ea => ea.GetEvent(It.IsAny<Type>())) .Returns((Type eventType) => { _registeredEvents.TryGetValue(eventType, out var targetEvent); return targetEvent; }); } // 返回已配置好的Mock实例 public Mock<IEventAggregator> GetEventAggregatorMock() { return _eventAggregatorMock; } // 创建并注册指定类型的PubSubEvent public PubSubEvent<T> CreateAndRegisterEvent<T>() where T : class { var eventInstance = new PubSubEvent<T>(); _registeredEvents.Add(typeof(PubSubEvent<T>), eventInstance); return eventInstance; } // 设置订阅回调并返回可触发的委托 public Action<T> SetupEventCallback<T>(PubSubEvent<T> targetEvent) where T : class { Action<T> capturedCallback = null; // 模拟订阅逻辑,捕获回调 targetEvent.Subscribe(action => capturedCallback = action, ThreadOption.PublisherThread); return capturedCallback; } }
2. 测试类中的正确调用方式
确保使用同一个辅助类实例完成Mock创建、事件注册和回调设置,保证实例引用一致:
public class MyViewModelTests { private readonly EventAggregatorMockHelper _eventHelper; private Action<string> _stringEventCallback; private MyViewModel _sut; public MyViewModelTests() { _eventHelper = new EventAggregatorMockHelper(); // 创建事件并捕获回调 var stringEvent = _eventHelper.CreateAndRegisterEvent<string>(); _stringEventCallback = _eventHelper.SetupEventCallback(stringEvent); // 初始化SUT,传入Mock实例 _sut = new MyViewModel(_eventHelper.GetEventAggregatorMock().Object); } [Fact] public void WhenStringEventIsPublished_StringPropertyUpdatesCorrectly() { // 触发事件回调 _stringEventCallback.Invoke("test content"); // 验证结果 Assert.Equal("test content", _sut.StringProperty); } }
3. 关键注意事项
- 禁止在辅助类的静态方法中创建Mock或事件实例,否则每次调用都会生成新对象,导致测试类使用的实例和辅助类操作的实例不一致。
- 确保
SetupEventCallback方法操作的是辅助类返回的同一个PubSubEvent实例,不要在测试类中重新调用GetEvent获取事件。 - 通过返回值将捕获的回调暴露给测试类,避免在辅助类内部用局部变量存储回调导致作用域隔离。
直接写测试类代码有效的原因
测试类构造函数中的所有变量(Mock、事件、回调)处于同一作用域,引用完全匹配;而未正确重构的辅助类会因为实例不共享、作用域隔离,导致设置回调的事件实例和SUT订阅的事件实例不是同一个,自然捕获不到有效回调。
内容的提问来源于stack exchange,提问作者user3265613
相关产品推荐
相关产品推荐

