3

我对单元测试用例编写完全陌生。我正在将 MVVMLigh 与 WPF 一起使用。是否有必要使用一些第三方测试框架或.Net 单元测试框架就够了?另外如何处理单元测试用例中的静态类?在这种情况下 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);
    }
}
4

1 回答 1

5

关于可测试性

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

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

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

关于设计

  1. MyViewModel 的构造函数做的太多了。构造函数应该很简单
  2. 如果依赖项为 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);
    }
}
于 2013-12-05T09:16:19.843 回答