使用Moq进行单元测试
2021年12月20日

使用Moq进行单元测试
我们的目标之一是为客户提供全面的解决方案. 软件开发项目有许多方面允许我们交付卓越的结果, 其中之一是花时间为自动化测试和验证创建健壮的机制.
单元测试
有许多方法可以验证软件,其中之一是单元测试. 这类测试集中于检查孤立的代码块或代码单元. 这些类型的测试不直接与基础设施(如数据库)交互, 文件, 网络, 外部服务, 等. 单元测试旨在隔离我们的代码,并提供一个可以直接测试输入的隔离环境, 输出, 以及代码中包含的开发者可以控制的行为.
公开课的计算器
{
Add(double item1, double item2)
{
返回item1 + item2;
}
}
一个例子是组成一个单元测试来评估执行简单加法的函数的行为. 对于这种单元测试, 我们将提供我们想要添加的数字,然后将单元测试产生的结果与手工计算的结果进行比较. 如果值匹配,则测试通过. 如果不匹配,就失败了,所以我们开始调查.
(TestMethod)
公共空间TestAdditionCalculationPasses ()
{
var calculator = new calculatoreexample.计算器();
Var结果=计算器.添加(2、3);
//断言将比较我们“知道”的结果
//测试单元返回的值
断言.AreEqual(结果);
}
这些单元测试可以按需执行,也可以作为自动化软件构建过程的一部分执行. 这些测试的结果可能导致该流程成功或失败. 在这两种情况下, 它通常会生成每个结果的报告,并在存在故障条件时提供额外的详细信息, 以及可能导致测试失败的原因的细节. 这种反馈帮助我们了解软件开发工作,并进行改进,使其变得更加健壮.
单元测试中使用Moq模拟
当我们讨论与单元测试相关的模拟时, 我们的目的是为超出给定单元测试范围的对象类提供替代.
例如, 我们可能想测试一个计算电子商务订单总成本的类方法. 成本计算的一部分包括与一个或多个外部运输供应商系统的通信. 在这种情况下, 我们不测试供应商的系统——只测试与之交互的代码——所以我们可以考虑模拟提供与供应商系统交互的对象.
需要明确的是,我们不希望执行与外部服务对话的单元测试. 如果出现这种情况,它将成为一个集成测试,而不再只是一个单元测试.
在本演示中,我们将使用 C# 类库, MSTest 用于单元测试,以及 Moq图书馆 的嘲笑. 运输服务将由IShipper接口表示. 这个接口是我们想要嘲笑的:
公共类秩序
{
public List<订单Item> 订单Items { get; set; }
public string Address { get; set; }
公共双GetItemTotal ()
{
返回订单Items.Sum(x => x.价格);
}
public double GetShippingTotal(isup托运人)
{
//我们需要联系一个外部服务来完成这个任务
var shippingCost =
托运人.CalculateShippingCost (
订单Items.Sum(x => x.重量),
这.Address
);
返回shippingCost;
}
public double Get订单Total(isup托运人)
{
var shippingTotal = GetShippingTotal(托运人);
var itemTotal = GetItemTotal ();
return shippingTotal + itemTotal;
}
}
对于我们的单元测试,我们关注于 Get订单Total 方法从我们 订单 类对象. 运输计算只是行为的一部分. 通过模拟船舶计算部分, 我们可以将这个代码单元从外部影响中分离出来,并测试我们控制下的部分代码.
有了一个mock,我们就可以训练mock在给定一个或多个特定输入时返回什么. 以这种方式模拟允许我们缩小单元测试的范围,这样外部关注就不会影响单元测试的结果.
(TestMethod)
公共空间Test订单TotalCalculation ()
{
//现在创建一个模拟对象,用于替代外部供应商发货系统
var shippingServiceMock = new Mock();
//然后定义替换行为
//这里我们告诉mock,任何calculateshipingcost的请求都可以接受
//任何匹配预期数据类型的参数,我们将始终让它返回45.98
//这可以根据需要进行调整,也可以设置为反映特定的输入值
shippingServiceMock
.Setup(x => x.CalculateShippingCost (
It.IsAny(),
It.IsAny()))
.返回(45.98);
}
如果我们对系统连接到供应商的系统时的行为感兴趣的话, 我们会考虑集成测试,因为我们测试的是整个集成,而不是包含集成的代码单元.
(TestMethod)
公共空间Test订单TotalCalculation ()
{
//现在创建一个模拟对象,用于替代外部供应商发货系统
var shippingServiceMock = new Mock();
//然后定义替换行为
//这里我们告诉mock,任何calculateshipingcost的请求都可以接受
//任何匹配预期数据类型的参数,我们将始终让它返回45.98
//这可以根据需要进行调整,也可以设置为反映特定的输入值
shippingServiceMock
.Setup(x => x.CalculateShippingCost (
It.IsAny(),
It.IsAny()))
.返回(45.98);
//从我们手工定义的订单数据中检索一个示例订单
var order = GenerateSample订单();
//计算订单中所有项目的小计
var orderItemTotal = order.GetItemTotal ();
//计算包含运费的订单总数
var orderTotal = order.Get订单Total (shippingServiceMock.对象);
//现在,我们确保Get订单Total方法的结果与我们在测试开始时在模拟设置中手动定义的结果相匹配
断言.AreEqual (45.98年,orderTotal);
}
与单元测试类似的是,通过按下刹车踏板并观察刹车钳是否按预期反应,来进行刹车是否正常工作的机械测试. 对你的刹车进行集成测试更像是把你的车开到停车场然后猛踩刹车. 隔离测试与在更现实的条件下测试是有很大区别的.
Summary
单元测试和模拟允许我们将代码与外部系统隔离,并测试我们在实际使用中可能遇到的各种场景. 当然, 我们不能预见所有的情况, 但随着开发人员, 我们可以设想许多最常见的方法,并编写单元测试来覆盖它们. 这一点努力帮助我们在部署软件之前捕获潜在的问题,总的来说帮助我们从编写更容易测试的代码的角度进行思考.