如何在被测类(SUT)构造函数中模拟Func<枚举,接口>参数
问题
在我的.Net6 Web应用中,我尝试为单个IWordRepository接口的两个实现注册依赖注入:
WordRepositoryInMemory:操作内存数据;WordRepositoryDatabase:调用数据库;
我创建了枚举来标识不同实现:
public enum WordRepositoryImplementation { WordRepositoryInMemory, WordRepositoryDatabase }
随后在Program.cs中注册两个服务及对应的解析委托:
builder.Services.AddScoped<WordRepositoryDatabase>(); builder.Services.AddScoped<WordRepositoryInMemory>(); builder.Services.AddTransient<Func<WordRepositoryImplementation, IWordRepository?>>(wordRepositoryProvider => key => { return key switch { WordRepositoryImplementation.WordRepositoryInMemory => wordRepositoryProvider.GetService<WordRepositoryInMemory>(), WordRepositoryImplementation.WordRepositoryDatabase => wordRepositoryProvider.GetService<WordRepositoryDatabase>(), _ => null, }; });
控制器通过构造函数接收该委托并获取指定实现:
private readonly IWordRepository _wordRepositoryDatabase; // 此控制器仅需调用其中一个实现 public DictionaryDataController(Func<WordRepositoryImplementation, IWordRepository> serviceResolver) { _wordRepositoryDatabase = serviceResolver(WordRepositoryImplementation.WordRepositoryDatabase); }
但这种实现导致控制器测试无法通过简单的Mock.Object实例化被测类(SUT)。我尝试设置Func<WordRepositoryImplementation, IWordRepository?>类型的字段来返回Mock对象,但写法有误:
_funcWordRepositoryImplementation= () => { return new Mock<WordRepositoryImplementation, IWordRepository>(); };
请问该如何正确设置这个字段?
解决方案
你需要创建一个返回Mock对象实例的委托,而不是直接MockFunc本身(或者更简单的是直接定义符合Func<WordRepositoryImplementation, IWordRepository?>签名的委托)。
方法1:直接构造符合签名的委托
先创建IWordRepository的Mock实例,然后定义Lambda表达式,当传入指定枚举值时返回这个Mock对象:
// 先创建数据库实现的Mock var mockDbRepo = new Mock<IWordRepository>(); // 可在这里配置Mock的行为,比如Setup方法 mockDbRepo.Setup(repo => repo.SomeMethod()).Returns("模拟数据"); // 定义符合签名的Func委托 Func<WordRepositoryImplementation, IWordRepository?> _funcWordRepositoryImplementation = key => { return key switch { WordRepositoryImplementation.WordRepositoryDatabase => mockDbRepo.Object, // 如果需要内存实现的Mock,可在此添加 // WordRepositoryImplementation.WordRepositoryInMemory => mockInMemoryRepo.Object, _ => null }; };
方法2:用Moq直接Mock Func委托(适用于更灵活的场景)
如果你想用Moq来Mock整个Func委托,可以这样写:
var mockFunc = new Mock<Func<WordRepositoryImplementation, IWordRepository?>>(); var mockDbRepo = new Mock<IWordRepository>(); // 设置当传入指定枚举值时,返回Mock的Repo对象 mockFunc.Setup(f => f(WordRepositoryImplementation.WordRepositoryDatabase)) .Returns(mockDbRepo.Object);
实例化控制器(SUT)的正确方式
不管用哪种方法,最终只需把委托实例传入控制器构造函数即可:
// 用方法1的委托实例化控制器 var controller = new DictionaryDataController(_funcWordRepositoryImplementation); // 或用方法2的Mock Func实例 var controller = new DictionaryDataController(mockFunc.Object);
这样就能正常实例化被测控制器,同时Mock对象也能按预期工作。
内容的提问来源于stack exchange,提问作者JRybelis
相关产品推荐
相关产品推荐

