单元测试MVVMLight Messenger

3

是否可以编写一个单元测试来调用Messenger.Default.Register方法,然后编写一个断言供Action使用?

我想确定当我的ViewModel调用某个命令的Execute方法后是否发送了正确的消息。

我尝试将Assert.AreEqual编写为Action,但这似乎无法正常工作。

2个回答

7
听起来这需要使用模拟对象!假设您将Messenger接口传递给您的ViewModel(因为依赖反转是一件好事,就是为了这个原因),如果我理解正确,您的代码应该像这样:

public class YourViewModel
{
    readonly IMessenger messenger;

    public YourViewModel(IMessenger messenger)
    {
        this.messenger = messenger;
        // setup of your delegate command to call Execute
    }

    void Execute(object parameter)
    {
        messenger.Send(new YourMessageType());
    }
}

然后在你的单元测试中,你需要模拟 messenger 并验证正确的方法是否被调用,这个例子中是 Send。因此,使用流行的模拟框架Moq:

public class YourViewModelTests
{
    [Test]
    public void Execute_Always_SendsYourMessageType()
    {
        // arrange
        var mockRepository = new MockRepository(MockBehavior.Loose);
        var mockMessenger = mockRepository.Create<IMessenger>();
        var systemUnderTest = new YourViewModel(mockMessenger.Object);

        // act
        systemUnderTest.YourCommand.Execute(null);

        // assert
        mockMessenger.Verify(p => p.Send<YourMessageType>(
                          It.Is(m => /* return true if it's the right message */)));
    }
}

通常我会将“安排”阶段的大部分内容放入测试设置方法中,但你应该能理解这个想法。
如果您仍然希望在不模拟信使的情况下使用,您可以执行以下操作:
public class YourViewModelTests
{
    [Test]
    public void Execute_Always_SendsYourMessageType()
    {
        // arrange
        var systemUnderTest = new YourViewModel();

        // Set the action to store the message that was sent
        YourMessageType actual;
        Messenger.Default.Register<YourMessageType>(this, t => actual = t);


        // act
        systemUnderTest.YourCommand.Execute(null);


        // assert
        YourMessageType expected = /* set up your expected message */;
        Assert.That(actual, Is.EqualTo(expected));
    }
}

非常感谢您的回复。有没有一种方法可以在不使用模拟或使用与PCL兼容的模拟库的情况下实现相同的效果? - Paul Diston
@PaulDiston 啊,这有点改变了情况。请看我的编辑,还有关于模拟和 PCL 兼容性的这个问题 - Patrick Quirk

3

另外,对于每个测试,可以创建一个单独的 Messenger 副本。对于运行时,您需要使用 Messenger 的默认实例,但对于单元测试,如我所说,为每个测试创建一个单独的副本:

        return new GalaSoft.MvvmLight.Messaging.Messenger(); // Unit Tests

        return GalaSoft.MvvmLight.Messaging.Messenger.Default; // Runtime

否则,您可能会重新发明轮子,因为在更复杂的情况下,需要测试ViewModel通信时,您将不得不管理Messenger订阅者、消息类型等。然后,可能需要编写用于Messenger模拟的单元测试,确保它以与原始Messenger相同的方式工作。在运行时和测试执行时比较时,Messenger引擎中没有任何不同之处。
因此,为了测试工厂返回Messenger的相同实例,测试方法订阅并等待,ViewModel发布;然后测试接受并退出。否则,测试超时并报告错误。我发现这种方法比模拟Messenger并通过模拟验证方法是否被调用更加“贴近现实”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接