使用 Caliburn 事件聚合器为包装 PublishOnUIThreadAsync 的方法设置最小起订量

Setup moq with Caliburn event aggregator for a method wrapping PublishOnUIThreadAsync

从我的 中,我了解到我必须执行 Mock.Setup 才能让 eventAggregatorMoq 了解 return 数据。

public void Navigate() {
    eventAggregator.PublishOnUIThreadAsync(new MyEvent(5));
}

下面的方法很好地覆盖了上面的包装器方法,它不是 awaiting 和 returning void.

eventAggregatorMock.Verify(_ => _.Publish(It.IsAny<MyEvent>(), 
                                It.IsAny<Action<System.Action>>()), Times.Once);

那么,如果我正在等待 returns 任务的方法,我该如何设置。

public async Task Navigate() {
    await eventAggregator.PublishOnUIThreadAsync(new MyEvent(5));
}

基于PublishOnUIThreadAsync扩展方法,其中returns一个Task

/// <summary>
/// Publishes a message on the UI thread asynchrone.
/// </summary>
/// <param name="eventAggregator">The event aggregator.</param>
/// <param name="message">The message instance.</param>
public static Task PublishOnUIThreadAsync(this IEventAggregator eventAggregator, object message) {
    Task task = null;
    eventAggregator.Publish(message, action => task = action.OnUIThreadAsync());
    return task;
}

您会注意到任务是在 Publish 执行的操作中分配的。您需要调用操作,以便通过确保调用 delegate/action 来分配任务。您可以在模拟方法的回调中执行此操作。

以下最小示例演示了如何执行此操作。请注意 Publish 是如何设置回调以调用传入的 Action 参数的。

[TestClass]
public class MyTestClass {

    [TestMethod]
    public async Task _EventAggregator_Should_Publish_OnUIThread() {
        //Arrange
        MyEvent navigateEvent = null;
        var eventAggregatorMock = new Mock<IEventAggregator>();
        eventAggregatorMock
            .Setup(x => x.Publish(It.IsAny<MyEvent>(), It.IsAny<Action<System.Action>>()))
            .Callback((object message, Action<System.Action> marshal) => {
                navigateEvent = (MyEvent)message;
                marshal(() => { });//Invoking action that would cause task to be assigned
            });
        var sut = new MyClass(eventAggregatorMock.Object);
        //Act
        await sut.Navigate();
        //Assert
        navigateEvent.Should().NotBeNull();
        eventAggregatorMock.Verify(_ => _.Publish(It.IsAny<MyEvent>(), 
            It.IsAny<Action<System.Action>>()), Times.Once);
    }

    public class MyEvent {
        private int p;

        public MyEvent(int p) {
            // TODO: Complete member initialization
            this.p = p;
        }

    }
    public class MyClass {
        IEventAggregator eventAggregator;
        public MyClass(IEventAggregator eventAggregator) {
            this.eventAggregator = eventAggregator;
        }

        public async Task Navigate() {
            await eventAggregator.PublishOnUIThreadAsync(new MyEvent(5));
        }
    }
}