单元测试之Mock(Moq)
Mock翻译为“嘲弄”,其实就是伪造一个对象用于测试。在单元测试中,被测试方法依赖于其他对象时,为了测试简单一般“伪造”一个这个对象;这样做的目的:
- 不用考虑依赖对象的复杂性(方便准备测试数据)
- 只专注测试被测试方法,不将单元测试扩充到测试依赖对象
打折算法测试
商场中的商品类:
public class Product {
public int ProductID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { set; get; }
}
打折接口:
public interface IDiscountHelper {
decimal ApplyDiscount(decimal totalParam);
}
被测试的打折算法
public class LinqValueCalculator : IValueCalculator {
private IDiscountHelper discounter;
public LinqValueCalculator(IDiscountHelper discountParam) {
discounter = discountParam;
}
public decimal ValueProducts(IEnumerable<Product> products) {
return discounter.ApplyDiscount(products.Sum(p => p.Price));
}
显然,被测试方法ValueProducts
依赖打折接口IDiscountHelper
,这里通过伪造一个实现IDiscountHelper
接口的对象。
Moq功能介绍
-
创建伪造对象
Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();
-
设置伪造对象的方法
mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>()))
-
参数限制
t.IsAny<decimal>()
通过It
限制这里列出几个限制表达式 解释 例子 It.Is (predicate) 通过predicate返回bool值判断限制测试 It.Is<decimal>(v => v == 0)
It.IsAny () T类型的任意值 It.IsAny()
It.IsInRange (TValue from, TValue to, Range rangeKind) T类型参数的值在范围内或外(rangeKind参数决定) It.IsInRange<int>(1, 100, Range.Inclusive)
-
设置返回值
.Returns<decimal>(total => total);
构造单元测试
[TestClass]
public class UnitTest2 {
private Product[] createProduct(decimal value) {
return new[] { new Product { Price = value } };
}
[TestMethod]
[ExpectedException(typeof(System.ArgumentOutOfRangeException))]
public void Pass_Through_Variable_Discounts() {
// arrange
Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();
mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>()))
.Returns<decimal>(total => total);
mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v == 0)))
.Throws<System.ArgumentOutOfRangeException>();
mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v > 100)))
.Returns<decimal>(total => (total * 0.9M));
mock.Setup(m => m.ApplyDiscount(It.IsInRange<decimal>(10, 100,
Range.Inclusive))).Returns<decimal>(total => total - 5);
var target = new LinqValueCalculator(mock.Object);
// act
decimal FiveDollarDiscount = target.ValueProducts(createProduct(5));
decimal TenDollarDiscount = target.ValueProducts(createProduct(10));
decimal FiftyDollarDiscount = target.ValueProducts(createProduct(50));
decimal HundredDollarDiscount = target.ValueProducts(createProduct(100));
decimal FiveHundredDollarDiscount = target.ValueProducts(createProduct(500));
// assert
Assert.AreEqual(5, FiveDollarDiscount, "$5 Fail");
Assert.AreEqual(5, TenDollarDiscount, "$10 Fail");
Assert.AreEqual(45, FiftyDollarDiscount, "$50 Fail");
Assert.AreEqual(95, HundredDollarDiscount, "$100 Fail");
Assert.AreEqual(450, FiveHundredDollarDiscount, "$500 Fail");
target.ValueProducts(createProduct(0));
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)