需要帮助在MVVMLight中使用Messaging编写单元测试用例

Max*_*com 3 static-methods unit-testing testcase mvvm-light

我是单元测试用例写作的新手.我在WPF中使用MVVMLigh.是否有必要使用一些第三方测试框架或.Net Unit测试框架呢?另外如何在单元测试用例中处理静态类?在这种情况下AppMessages类.

有人可以指导我如何编写以下代码的单元格:

public MyViewModel(Participant participant)
{    
    if (participant != null)
    {
        this.ParentModel = parentModel;
        OkCommand = new RelayCommand(() => OkCommandExecute());
        CalculateAmountCommand = new RelayCommand(() => CalculateAmount());        
    }
    else
    {
        ExceptionLogger.Instance.LogException(Constants.ErrorMessages.FinancialLineCanNotBeNull, "FinancialLineViewModel");
        AppMessages.DisplayDialogMessage.Send(Constants.ErrorMessages.FinancialLineCanNotBeNull, MessageBoxButton.OK, Constants.DefaultCaption, null);
    }
}

public static class AppMessages
{
    enum AppMessageTypes
    {
        FinancialLineViewDisplay,
        FinancialLineViewClose,
        DisplayDialogMessage
    }

    public static class DisplayDialogMessage
    {
        public static void Send(string message, MessageBoxButton button, string caption, System.Action<MessageBoxResult> action)
        {
            DialogMessage dialogMessage = new DialogMessage(message, action)
            {
                Button = button,
                Caption = caption
            };

            Messenger.Default.Send(dialogMessage, AppMessageTypes.DisplayDialogMessage);
        }

        public static void Register(object recipient, System.Action<DialogMessage> action)
        {
            Messenger.Default.Register<DialogMessage>(recipient, AppMessageTypes.DisplayDialogMessage, action);
        }
    }
}

public class ExceptionLogger
{
    private static ExceptionLogger _logger;
    private static object _syncRoot = new object();

    public static ExceptionLogger Instance
    {
        get
        {
            if (_logger == null)
            {
                lock (_syncRoot)
                {
                    if (_logger == null)
                    {
                        _logger = new ExceptionLogger();
                    }
                }
            }

            return _logger;
        }
    }

    public void LogException(Exception exception, string additionalDetails)
    {
        LogException(exception.Message, additionalDetails);
    }

    public void LogException(string exceptionMessage, string additionalDetails)
    {
        MessageBox.Show(exceptionMessage);
    }
}
Run Code Online (Sandbox Code Playgroud)

Vla*_*aev 5

关于可测试性

由于使用单例和静态类MyViewModel是不可测试的.单元测试是关于隔离的.如果要对某些类(例如,MyViewModel)进行单元测试,则需要能够通过test double(通常是stub或mock)替换其依赖项.只有在代码中提供接缝时才会出现此功能.用于提供接缝的最佳技术之一是依赖注入.学习DI的最佳资源是Mark Seemann(.NET中的依赖注入)这本书.

您无法轻易替换静态成员的调用.因此,如果您使用许多静态成员,那么您的设计并不完美.

当然,您可以使用无约束隔离框架(如Typemock Isolator,JustMock或Microsoft Fakes)来伪造静态方法调用,但这需要花钱,而且不会让您更好地设计.这些框架非常适合为遗留代码创建测试工具.

关于设计

  1. MyViewModel的构造函数做得太多了.构造函数应该很简单.
  2. 如果dependecy为null,则构造函数必须抛出ArgumentNullException但不以静默方式记录错误.抛出异常清楚地表明您的对象不可用.

关于测试框架

您可以使用任何您喜欢的单元测试框架.即使是MSTest,但我个人并不推荐它.NUnit和xUnit.net好多了.

进一步阅读

  1. Mark Seeman - .NET中的依赖注入
  2. Roy Osherove - 单元测试的艺术(第2版)
  3. Michael Feathers - 有效地处理遗留代码
  4. Gerard Meszaros - xUnit测试模式

样本(使用MvvmLight,NUnit和NSubstitute)

public class ViewModel : ViewModelBase
{
    public ViewModel(IMessenger messenger)
    {
        if (messenger == null)
            throw new ArgumentNullException("messenger");

        MessengerInstance = messenger;
    }

    public void SendMessage()
    {
        MessengerInstance.Send(Messages.SomeMessage);
    }
}

public static class Messages
{
    public static readonly string SomeMessage = "SomeMessage";
}

public class ViewModelTests
{
    private static ViewModel CreateViewModel(IMessenger messenger = null)
    {
        return new ViewModel(messenger ?? Substitute.For<IMessenger>());
    }

    [Test]
    public void Constructor_WithNullMessenger_ExpectedThrowsArgumentNullException()
    {
        var exception = Assert.Throws<ArgumentNullException>(() => new ViewModel(null));
        Assert.AreEqual("messenger", exception.ParamName);
    }

    [Test]
    public void SendMessage_ExpectedSendSomeMessageThroughMessenger()
    {
        // Arrange
        var messengerMock = Substitute.For<IMessenger>();
        var viewModel = CreateViewModel(messengerMock);

        // Act
        viewModel.SendMessage();

        // Assert
        messengerMock.Received().Send(Messages.SomeMessage);
    }
}
Run Code Online (Sandbox Code Playgroud)