使用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
显然这真的很乏味(我有几十个课要做)。有没有更好的方法呢?
答案 0 :(得分:1)
这有点繁琐,我不确定这是否是正确的测试策略,但这里有一种似乎有用的方法。我在我的测试类中创建了一个函数,如下所示:
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
的通用方法。
答案 1 :(得分:0)
我仍然不清楚为什么你需要模拟IMyFactory
,但要回答你的核心问题:不,Moq现在提供了为任何泛型类型参数设置泛型方法的方法。我参与了一个与Moq集成的开源项目,我之前遇到过这个限制。
但您应该只为与测试相关的泛型类型参数设置方法。如果您的测试需要在工厂中设置一个充满类型参数的手,那么这听起来像代码气味或异国情调的边缘情况。