单元测试时应该如何处理嵌套的ViewModel?

问题描述:

我正在为我的项目中的ViewModel创建一些单元测试。我没有遇到任何问题,因为其中大多数都非常简单,但是当我在其他ViewModel中有一个新的(未完成的)ViewModel时遇到了问题。单元测试时应该如何处理嵌套的ViewModel?

public class OrderViewModel : ViewModelBase 
{ 
    public OrderViewModel(IDataService dataService, int orderId) 
    { 
     \\ ... 

     Payments = new ObservableCollection<PaymentViewModel>(); 
    } 

    public ObservableCollection<PaymentViewModel> Payments { get; private set; } 

    public OrderStatus Status { ... } //INPC 

    public void AddPayment() 
    { 
     var vm = new PaymentViewModel(); 

     payments.Add(vm); 

     // TODO: Subscribe to PaymentViewModel.OnPropertyChanged so that 
     // if the payment is valid, we update the Status to ready. 
    } 
} 

我想创建一个单元测试,这样,如果我的任何PaymentViewModelIsValid属性更改和所有的人都不错,Status应该OrderStatus.Ready。我可以实现这个类,但是让我担心的是如果问题出现在PaymentViewModel中,我的单元测试将会中断。

我不确定这是否正常,但它只是觉得我不应该担心PaymentViewModel是否正常运行,以便我的单元测试OrderViewModel是正确的。

public void GivenPaymentIsValidChangesAndAllPaymentsAreValid_ThenStatusIsReady() 
{ 
    var vm = new OrderViewModel(); 

    vm.AddPayment(); 
    vm.AddPayment(); 

    foreach (var payment in vm.Payments) 
    { 
     Assert.AreNotEqual(vm.Status, OrderStatus.Ready); 

     MakePaymentValid(payment); 
    } 

    // Now all payments should be valid, so the order status should be ready. 
    Assert.AreEqual(vm.Status, OrderStatus.Ready); 
} 

的问题是,我怎么写MakePaymentValid在保证了我PaymentViewModel的行为不会负我的单元测试影响这样的方式?因为如果是这样,那么我的单元测试就会失败,因为另一段代码不起作用,而不是我的代码。或者,如果PaymentViewModel错误,它应该失败吗?我只是被撕裂,我不认为如果PaymentViewModel有一个错误,我的OrderViewModel测试应该失败。

我意识到我总是可以创建一个接口,就像我如何处理IDataService一样,但是在我看来,让每个ViewModel都有一个接口并注入一些方法会有点矫枉过正?

+0

如果我理解正确。您可以为PaymentViewModel创建一个单独的测试,如果存在可能与PaymentViewModel中的IsValid属性相关的内容,则保留此项。另一方面,如果IsValid是一个没有任何业务逻辑的简单设置器,那么您将主要通过创建一个单独的测试来测试ObservableCollection,这似乎是多余的。 –

说到单元测试,您绝对应该将测试与任何外部依赖分离开来。请记住,这并不意味着你必须通过一些界面;你会遇到你正在使用特定课程的情况,无论该课程是处于或不在你的控制之下。

想象一下,而不是你的榜样,你依靠DateTime.Now。有些人会主张将其抽象成某种接口IDateTimeService,这可以起作用。或者,您可以利用Microsoft Fakes:Shims &存根。

Microsoft Fakes将允许您创建Shim *实例。在这个主题上有很多需要解答的问题,但微软提供的图像说明了Fakes的使用超出了你的控制范围(它也包括了你的控制中的组件)。

Microsoft Fakes Shims/Stubs

通知如何要测试的组分(OrderViewModel)应当从System.dll中(即DateTime.Now),其它组分(PaymentViewModel)和外部件中分离,以及(如果依赖于数据库或网络服务)。 Shim用于伪造类,而Stub用于伪造(嘲弄)界面。


一旦你添加一个假货组件,只需使用ShimPaymentViewModel类,以提供您预计它应该的行为。如果出于某种原因,真正的PaymentViewModel课程行为异常并且您的应用程序崩溃,您至少可以确信问题不是由OrderViewModel引起的。当然,为了避免这种情况,您应该包含一些针对PaymentViewModel的单元测试,以确保它的行为正确,无论其他类正在使用它或他们如何利用它。

TL; DR;

是的,当涉及到利用Microsoft Fakes进行测试时,完全隔离您的组件。哦,微软的假货和其他的框架很好地搭配,所以不要用它来表达你自己的选择;它与其他框架一起工作。