如何Mock带有IEquatable<T>类型约束的泛型方法?
问题:Mock带泛型约束的泛型方法
Push<T> 我在C#项目中使用xUnit和Moq4进行单元测试,需要Mock的接口如下:
public interface IEngine { void Push<T>() where T : IEquatable<T>; }
我希望按如下方式创建并设置该接口的Mock:
[Fact] public void My_test() { var value = 0; var mock = new Mock<IEngine>(); mock.Setup(m => m.Push<???>()).Callback(() => value++); mock.Object.Push<TypeOne>(); mock.Object.Push<TypeTwo>(); Assert.Equal(2, value); }
其中TypeOne和TypeTwo的定义为:
public class TypeOne : IEquatable<TypeOne> { public bool Equals(TypeOne? other) => true; } public class TypeTwo : IEquatable<TypeTwo> { public bool Equals(TypeTwo? other) => true; }
我应该用什么替换代码中的???来让测试编译并正常运行?这是否可行?
我曾尝试如下写法:
mock.Setup(m => m.Push<IEquatable<It.IsAnyType>>()).Callback(() => value++);
但编译报错,错误信息为:
The type 'System.Equatable<Moq.It.IsAnyType>' cannot be used as type parameter 'T' in the generic type or method 'IEngine.Push
()'. There is no implicit reference conversion from 'System.Equatable<Moq.It.IsAnyType>' to 'System.Equatable<System.Equatable<Moq.It.IsAnyType>>'.
我了解过Moq4中的ITypeMatcher接口和TypeMatcherAttribute,但不知道如何在该场景中应用,恳请帮助。
解决方案
要Mock所有符合IEquatable<T>约束的泛型Push<T>调用,可使用Moq的It.IsAnyType结合泛型约束匹配,具体写法如下:
mock.Setup(m => m.Push<It.IsAnyType>()) .Where(T => typeof(IEquatable<>).MakeGenericType(T).IsAssignableFrom(T)) .Callback(() => value++);
说明:
It.IsAnyType用于匹配任意泛型类型参数.Where(T => ...)是Moq提供的泛型约束匹配方法,这里检查类型T是否实现了IEquatable<T>(与IEngine.Push<T>的约束一致)- 配置完成后,所有符合约束的
Push<T>调用都会触发Callback逻辑,TypeOne和TypeTwo的调用都会让value自增,最终断言会通过。
如果只需匹配特定类型,也可以直接指定:
// 匹配TypeOne的Push调用 mock.Setup(m => m.Push<TypeOne>()).Callback(() => value++); // 匹配TypeTwo的Push调用 mock.Setup(m => m.Push<TypeTwo>()).Callback(() => value++);
但如果需要批量匹配所有符合约束的类型,第一种方法更高效。
内容的提问来源于stack exchange,提问作者Dka8
相关产品推荐
相关产品推荐

