如何使用依赖注入模拟泛型工厂
本文关键字:泛型 工厂 模拟 注入 何使用 依赖 | 更新日期: 2023-09-27 18:36:13
使用Ninject,我有一个像这样的工厂注册:
kernel.Bind<IMyFactory>().ToFactory();
IMyFactory
看起来像这样的地方:
public interface IMyFactory
{
T CreateInstance<T> where T : MyBaseClass;
}
这样做的原因是从MyBaseClass
派生的类有自己的依赖项,我需要能够从它们的类型创建这些类的实例,我这样做是这样的:
MethodInfo mi = factory.GetType().GetMethod("CreateFoo");
MethodInfo generic = mi.MakeGenericMethod(type);
var param = (MyBaseClass)generic.Invoke(factory, null);
其中factory
是 Ninject 创建的IMyFactory
实例,type
是我要创建的派生类的类型MyBaseClass
。这一切都非常有效。
问题是我希望能够使用 Visual Studio 和 Moq 中的测试框架对此进行单元测试,但我找不到模拟IMyFactory
的好方法。
目前我有这个:
myFactory = new Mock<IMyFactory>();
myFactory.Setup(d => d.CreateFoo<MyFirstClass>()).Returns(new MyFirstClass(userService.Object));
myFactory.Setup(d => d.CreateFoo<MySecondClass>()).Returns(new MySecondClass(userService.Object, someOtherServiceMock.Object));
// repeat for as many classes as would usually be handled by IMyFactory
显然,这真的很乏味(我有几十节课要做)。有没有更好的方法?
这有点繁琐,我不确定这是否是正确的测试策略,但这里有一种方法似乎有效。我在测试类中创建了一个函数,如下所示:
private Dictionary<Type, Mock> mocks;
private void SetupCreateFoo<T>(Mock<IMyFactory> myFactory) where T: MyBaseClass
{
var t = typeof(T);
var constructors = from constructor in t.GetConstructors()
let parameters = constructor.GetParameters()
where parameters.Length > 0
select new { constructor, parameters };
// Note: there should only be one constructor anyway
foreach (var c in constructors)
{
List<object> parameters = new List<object>();
foreach (var p in c.parameters)
{
parameters.Add(mocks[p.ParameterType].Object);
}
myFactory.Setup(d => d.CreateAnalytic<T>()).Returns((T)c.constructor.Invoke(parameters.ToArray()));
}
}
其中字典mocks
是从 MyBaseClass
派生的类可能需要的模拟服务的集合。
现在,在设置测试类的位置,我可以执行以下操作:
[TestInitialize]
public void Setup()
{
userService = new Mock<IUserService>();
//... setup userService
someOtherServiceMock = new Mock<ISomeOtherService>();
//... setup someOtherService
mocks = new Dictionary<Type, Mock>()
{
{ typeof(IUserService), userService },
{ typeof(ISomeOtherService), someOtherServiceMock}
};
myFactory= new Mock<IMyFactory>();
SetupCreateFoo<MyFirstClass>(myFactory);
SetupCreateFoo<MySecondClass>(myFactory);
// ... etc
finderService = new FinderService(userService.Object, myFactory.Object);
}
而且我认为我可能会设置一个List<Type>
并在循环中调用SetupCreateFoo<T>
以进一步减少重复,但是使用 Type
调用通用方法需要再次进行一些反思。
我仍然不清楚为什么你需要嘲笑IMyFactory
,但要回答你的核心问题:不,Moq 现在提供了为任何泛型类型参数设置泛型方法的方法。我为一个与Moq集成的开源项目做出了贡献,我以前也遇到过这个限制。
但是,您应该只为与测试相关的泛型类型参数设置方法。如果你的测试需要在工厂设置满是类型参数的手,那么这听起来要么像是代码异味,要么是异国情调的边缘情况。