如何在单元测试中跳过构造函数中的数据库操作?
解决单元测试时跳过构造函数数据库操作的方案
方案一:依赖注入解耦(推荐)
把数据库操作逻辑抽象成接口,让OrderVM通过构造函数依赖这个接口,测试时注入模拟实现,彻底隔离数据库依赖。
- 定义产品数据访问接口
public interface IProductRepository { List<Product> GetAllProducts(); }
- 实现生产环境用的仓库类
public class ProductRepository : IProductRepository { public List<Product> GetAllProducts() { using (var db = new DatabaseContext()) { return db.Products_t.ToList(); } } }
- 修改
OrderVM的构造函数,支持依赖注入
public class OrderVM { public ObservableCollection<Product> Products_o { get; set; } public decimal SubTotal { get; set; } public decimal Tax { get; set; } // 生产环境默认构造函数,自动注入真实仓库 public OrderVM() : this(new ProductRepository()) { } // 测试专用构造函数,接受自定义仓库实现 public OrderVM(IProductRepository productRepo) { var productList = productRepo.GetAllProducts(); Products_o = new ObservableCollection<Product>(productList); } public void calTax() { Tax = SubTotal * 5 / 100; } }
- 单元测试中使用模拟仓库(以Moq为例)
public class UnitTest1 { [Fact] public void Calculate_Tax() { // 模拟仓库,返回空列表即可,因为calTax不依赖产品数据 var mockRepo = new Mock<IProductRepository>(); mockRepo.Setup(repo => repo.GetAllProducts()).Returns(new List<Product>()); var order = new OrderVM(mockRepo.Object); order.SubTotal = 100; order.calTax(); order.Tax.Should().Be(5); } }
方案二:添加测试专用构造函数(快速临时方案)
如果不想大规模重构,给OrderVM加一个内部构造函数,跳过数据库初始化逻辑,测试项目通过InternalsVisibleTo访问这个构造函数。
- 修改
OrderVM,添加测试构造函数
public class OrderVM { public ObservableCollection<Product> Products_o { get; set; } public decimal SubTotal { get; set; } public decimal Tax { get; set; } public OrderVM() { using (var db = new DatabaseContext()) { var list = db.Products_t.ToList(); Products_o = new ObservableCollection<Product>(list); } } // 测试专用构造函数,标记为内部访问 internal OrderVM(bool skipDbInit) { // 初始化空集合,避免空引用异常 Products_o = new ObservableCollection<Product>(); } public void calTax() { Tax = SubTotal * 5 / 100; } }
- 在主项目的
.csproj中添加配置,允许测试项目访问内部成员
<ItemGroup> <InternalsVisibleTo Include="你的测试项目名称" /> </ItemGroup>
- 编写单元测试
public class UnitTest1 { [Fact] public void Calculate_Tax() { // 使用测试专用构造函数,跳过数据库操作 var order = new OrderVM(skipDbInit: true); order.SubTotal = 100; order.calTax(); order.Tax.Should().Be(5); } }
方案三:使用内存数据库(适合需要初始化产品数据的场景)
如果测试时需要Products_o有真实结构但不想依赖物理数据库,可以用EF Core的内存数据库替代。
- 修改
DatabaseContext,支持传入DbContextOptions
public class DatabaseContext : DbContext { public DatabaseContext() { } public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { } public DbSet<Product> Products_t { get; set; } }
- 单元测试中创建内存数据库
public class UnitTest1 { [Fact] public void Calculate_Tax() { // 配置内存数据库 var options = new DbContextOptionsBuilder<DatabaseContext>() .UseInMemoryDatabase(databaseName: "Test_OrderDb") .Options; // 提前创建表结构 using (var db = new DatabaseContext(options)) { db.Database.EnsureCreated(); } // 此时创建OrderVM会使用内存数据库,不会报错 var order = new OrderVM(); order.SubTotal = 100; order.calTax(); order.Tax.Should().Be(5); } }
内容的提问来源于stack exchange,提问作者Kave
相关产品推荐
相关产品推荐

