在进行单元测试时,您应该如何处理嵌套的ViewModel?

时间:2015-10-13 17:10:13

标签: c# unit-testing mvvm tdd

我正在为我的项目中的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.
    }
}

我想创建一个单元测试,以便在我的PaymentViewModel IsValid属性中的任何一个属性发生更改且所有属性都为真时Status应为{{1} }}。我可以实现这个类,但令我担心的是,如果问题出现在OrderStatus.Ready中,我的单元测试会中断。

我不确定这是否正常,但我觉得我不应该担心PaymentViewModel是否正常运行才能进行{{1}的单元测试是正确的。

PaymentViewModel

问题是,如何以这样的方式编写OrderViewModel,以保证PaymentViewModel的行为不会对我的单元测试产生负面影响?因为如果确实如此,那么我的单元测试将根据另一段不起作用的代码而不是我的代码而失败。或者,如果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的测试失败。

我意识到我总是可以像OrderViewModel一样创建一个界面,但在我看来,让每个ViewModel都有一个接口并注入一些如何有点过分了?

1 个答案:

答案 0 :(得分:3)

在单元测试方面,你绝对应该将测试与任何外部依赖关系分开。请记住,这并不意味着您必须传递一些界面;你将遇到你正在使用某个特定类的情况,无论该类是否在你的控制之内。

想象一下,你不依赖于你的例子,而是依靠DateTime.Now。有些人会争辩将它抽象成某种界面IDateTimeService,这可能有用。或者,您可以利用Microsoft Fakes:Shims&amp;存根。

Microsoft Fakes将允许您创建Shim *实例。关于这个主题有很多可以讨论,但微软提供的图像说明Fakes的使用超出了你控制范围之外的类(它还包括你控制中的组件)。

Microsoft Fakes Shims/Stubs

注意您正在测试的组件(OrderViewModel)应该如何与System.dll(即DateTime.Now),其他组件(PaymentViewModel)以及外部项目隔离(如果你依赖数据库或Web服务)。 Shim用于伪造类,而Stub用于伪造(模拟)接口。

添加Fakes程序集后,只需使用ShimPaymentViewModel类来提供您期望的行为。如果由于某种原因,真正的PaymentViewModel类行为不端并且您的应用程序崩溃,至少可以确保问题不是由OrderViewModel引起的。当然,为了避免这种情况,你应该为PaymentViewModel包含一些单元测试,以确保它的行为正常,无论其他类正在使用它还是如何使用它。

TL; DR;

是的,在利用Microsoft Fakes进行测试时,完全隔离了您的组件。哦,微软Fakes确实可以很好地与其他框架一起使用,所以不要因为使用它而感觉到你已经提到了其他框架;它与其他框架一起使用。