在没有指定T的情况下模拟Moq中的generics方法

我有一个方法的接口如下:

public interface IRepo { IA Reserve(); } 

我想模拟包含此方法的类,而不必为它可用于的每种类型指定安装方法。 理想情况下,我只是希望它返回一个new mock.Object

我该如何实现这一目标?

看来我的解释还不清楚。 这是一个例子 – 当我指定T(这里是字符串)时,这是可能的:

 [TestMethod] public void ExampleTest() { var mock = new Mock(); mock.Setup(pa => pa.Reserve()).Returns(new Mock<IA>().Object); } 

我想要实现的是这样的:

 [TestMethod] public void ExampleTest() { var mock = new Mock(); mock.Setup(pa => pa.Reserve()).Returns(new Mock<IA>().Object); // of course T doesn't exist here. But I would like to specify all types // without having to repeat the .Setup(...) line for each of them. } 

被测对象的某些方法可能会为三种或四种不同类型调用reserve。 如果我必须设置所有类型,我必须为每个测试编写大量的设置代码。 但是在单个测试中,我并不关心所有这些,我只需要非空模拟对象,除了我实际测试的那个(我很乐意写一个更复杂的设置)。

除非我误解了你的需要,否则你可以构建一个这样的方法:

 private Mock MockObject() { var mock = new Mock(); return mock.Setup(pa => pa.Reserve()) .Returns(new Mock>().Object).Object; } 

只需这样做:

 [TestMethod] public void ExampleTest() { var mock = new Mock { DefaultValue = DefaultValue.Mock, }; // no setups needed! ... } 

由于您的模拟没有Strict行为,因此您对尚未设置的调用感到满意。 在这种情况下,只返回“默认”。 然后

 DefaultValue.Mock 

确保此“默认”是适当类型的新Mock<> ,而不仅仅是空引用。

这里的限制是您无法控制(例如,进行特殊设置)返回的单个“子模拟”。

我发现了一种我认为更接近你想要的替代方案。 无论如何它对我有用,所以这里。 我们的想法是创建一个几乎纯粹抽象的中间类,并实现您的界面。 不抽象的部分是Moq无法处理的部分。 例如

 public abstract class RepoFake : IRepo { public IA Reserve() { return (IA)ReserveProxy(typeof(T)); } // This will be mocked, you can call Setup with it public abstract object ReserveProxy(Type t); // TODO: add abstract implementations of any other interface members so they can be mocked } 

现在你可以模拟RepoFake而不是IRepo。 除了你在ReserveProxy而不是Reserve上编写你的设置之外,一切都是一样的。 如果要基于类型执行断言,可以处理回调,尽管ReserveProxyType参数是完全可选的。

这是一种似乎有用的方法。 如果您在IRepo中使用的所有类都inheritance自单个基类,则可以按原样使用它,而不必更新它。

 public Mock SetupGenericReserve() where TBase : class { var mock = new Mock(); var types = GetDerivedTypes(); var setupMethod = this.GetType().GetMethod("Setup"); foreach (var type in types) { var genericMethod = setupMethod.MakeGenericMethod(type) .Invoke(null,new[] { mock }); } return mock; } public void Setup(Mock mock) where TDerived : class { // Make this return whatever you want. Can also return another mock mock.Setup(x => x.Reserve()) .Returns(new IA()); } public IEnumerable GetDerivedTypes() where T : class { var types = new List(); var myType = typeof(T); var assemblyTypes = myType.GetTypeInfo().Assembly.GetTypes(); var applicableTypes = assemblyTypes .Where(x => x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract && x.GetTypeInfo().IsSubclassOf(myType)); foreach (var type in applicableTypes) { types.Add(type); } return types; } 

否则,如果您没有基类,则可以修改SetupGenericReserve以不使用TBase类型参数,而只是创建要设置的所有类型的列表,如下所示:

 public IEnumerable Alternate() { return new [] { MyClassA.GetType(), MyClassB.GetType() } } 

注意:这是为ASP.NET Core编写的,但除了GetDerivedTypes方法之外,还应该适用于其他版本。