如何对视图模型使用方法进行单元测试
本文关键字:单元测试 使用方法 模型 视图 | 更新日期: 2023-09-27 18:36:41
我想测试一个将 ViewModel 作为参数的帮助程序方法。我遇到的问题是测试似乎需要我实例化并分配我的 ViewModel 使用的所有模型。在我下面给出的示例中,这没什么大不了的,因为只有几个,但在我正在处理的实际 VM 中,有很多。有没有其他方法可以做到这一点,这样我就不必创建和分配每个对象?
用于说明目的的示例代码...
模型
public class Meal
{
public int MealID { get; set; }
public string Title { get; set; }
public decimal Cost { get; set; }
}
public class Beverage
{
public int BeverageID { get; set; }
public string Title { get; set; }
public decimal Cost { get; set; }
}
public class Desert
{
public int DesertID { get; set; }
public string Title { get; set; }
public decimal Cost { get; set; }
}
视图模型
public class DinnerViewModel
{
public Meal Meal { get; set; }
public Beverage Beverage { get; set; }
public Desert Desert { get; set; }
public decimal SalesTax { get; set; }
public bool SeniorDiscount { get; set; }
}
帮手
public class Calculator
{
public decimal Total(DinnerViewModel dvm)
{
decimal subtotal = dvm.Meal.Cost + dvm.Beverage.Cost + dvm.Desert.Cost;
if (dvm.SeniorDiscount)
{
subtotal = subtotal - (subtotal * 0.1M);
}
return subtotal + (subtotal * dvm.SalesTax);
}
}
测试
[TestMethod]
public void CalculatorReturnsCorrectTotalForNonSenior()
{
DinnerViewModel dvm = new DinnerViewModel();
dvm.Meal.Cost = 7M;
dvm.Beverage.Cost = 1M;
dvm.Desert.Cost = 2M;
dvm.SalesTax = 0.08M;
dvm.SeniorDiscount = false;
Calculator calc = new Calculator();
decimal expected = 10.80M;
decimal actual = calc.Total(dvm);
Assert.AreEqual(expected, actual, "The actual value does not match the expected value.");
}
这会导致"空引用异常"错误。正如我所说,我可以创建和分配必要的对象...
[...]
Meal meal = new Meal();
dvm.Meal = meal;
dvm.Meal.Cost = 7M;
[...]
。而且,一旦它们全部完成,测试就会成功 - 但这似乎在更大的 VM 上需要大量的工作,我有一种感觉,我可以做些什么来简化这一点。
您会看到 NullReferenceException,因为在实例化 DinnerViewModel 时,Meal、Beverage 和 Desert 实例不会分配给相应的属性。因此,所有这些属性都是 null
.在访问这些属性之前,应创建并分配新对象。您可以使用视图模型的构造函数:
public class DinnerViewModel
{
public DinnerViewModel()
{
Meal = new Meal();
Beverage = new Beverage();
Desert = new Desert();
}
public Meal Meal { get; set; }
public Beverage Beverage { get; set; }
public Desert Desert { get; set; }
public decimal SalesTax { get; set; }
public bool SeniorDiscount { get; set; }
}
我还喜欢创建一些返回测试存根的辅助方法。它消除了重复并使您的测试清晰:
private DinnerViewModel CreateTenDollarsDinner()
{
return new DinnerViewModel {
Meal = new Meal { Cost = 7M },
Beverage = new Beverage { Cost = 1M },
Desert = new Desert { Cost = 2M },
SalesTax = 0.08M,
SeniorDiscount = false
};
}
[TestMethod]
public void CalculatorReturnsCorrectTotalForNonSenior()
{
DinnerViewModel dvm = CreateTenDollarsDinner();
Calculator calc = new Calculator();
Assert.AreEqual(10.80M, calc.Total(dvm));
}
要么
你的模型(Meal,Beaverage和Dessert)也是ViewModels,要么你的DinnerViewModel不是真正的ViewModel。
视图模型的目的是为视图提供直接可用的值。
根据单元测试示例,将 Calculator.Total 帮助程序方法直接放入 DinnerViewModel,并将总计算值作为属性公开给视图会更合适:
public class DinerViewModel
{
public Meal Meal { get; set; }
public Beverage Beverage { get; set; }
public Desert Desert { get; set; }
public decimal SalesTax { get; set; }
public bool SeniorDiscount { get; set; }
public decimal TotalCostOfDinner
{
get
{
decimal subtotal = 0M;
if(Meal != null) subtotal += Meal.Cost;
if(Beverage != null) subtotal += Beverage.Cost;
if(Desert != null) subtotal += Desert.Cost;
if (SeniorDiscount) subtotal -= subtotal * 0.1M;
return subtotal + (subtotal * SalesTax);
}
}
}
现在,您的视图可以直接从 DinnerView 模型中获取正确的 TotalCostOfDinner。
现在进行单元测试:
[TestMethod]
public void TotalCostOfDinnerReturnsCorrectTotalForNonSenior()
{
DinnerViewModel dvm = new DinnerViewModel
{
Meal = new Meal { Cost = 7M },
Beverage = new Beverage { Cost = 1M },
Desert = new Desert { Cost = 2M },
SalesTax = 0.08M,
SeniorDiscount = false
};
decimal expected = 10.80M;
decimal actual = dvm.TotalCostOfDinner;
Assert.AreEqual(expected, actual, "The actual value does not match the expected value.");
}