如何实现Mock类的继承复用?解决接口继承场景下Mock代码非DRY问题
解决Mock类继承复用的DRY问题
你遇到的这个问题确实很典型——当Mock继承接口时,直接继承Mock类会因为类型约束卡住,导致无法复用基类的Mock配置。下面给你几个实用的解决方案:
方案1:提取基类Mock配置到辅助方法
把Base接口的Mock配置逻辑抽成静态扩展方法,这样不管是BaseMock还是DerivedMock都可以调用,避免代码重复:
public static class BaseMockSetup { public static void SetupBaseProperties<T>(this Mock<T> mock, int basePropertyValue) where T : class, IBase { mock.SetupGet(o => o.BaseProperty).Returns(basePropertyValue); } } // BaseMock可以简化成这样 class BaseMock : Mock<IBase> { public int BaseProperty { get; set; } = 42; public BaseMock() { this.SetupBaseProperties(BaseProperty); } } // DerivedMock不用重复Base的配置,只需要调用辅助方法+配置自己的属性 class DerivedMock : Mock<IDerived> { public int BaseProperty { get; set; } = 42; public int DerivedProperty { get; set; } = 73; public DerivedMock() { this.SetupBaseProperties(BaseProperty); SetupGet(o => o.DerivedProperty).Returns(DerivedProperty); } }
这个方案的好处是灵活,不管你的Mock类继承哪个接口,只要实现了IBase,就能复用Base的配置逻辑,完全符合DRY原则。
方案2:使用泛型基类Mock
把BaseMock改成泛型类,约束泛型参数必须继承IBase,这样DerivedMock可以直接继承这个泛型类,同时保留对IDerived的类型支持:
// 泛型基类Mock,支持所有继承IBase的接口 class BaseMock<T> : Mock<T> where T : class, IBase { public int BaseProperty { get; set; } = 42; public BaseMock() { SetupGet(o => o.BaseProperty).Returns(BaseProperty); } } // DerivedMock直接继承泛型BaseMock,指定类型为IDerived class DerivedMock : BaseMock<IDerived> { public int DerivedProperty { get; set; } = 73; public DerivedMock() { SetupGet(o => o.DerivedProperty).Returns(DerivedProperty); } }
这个方案更符合面向对象的继承思想,DerivedMock自动获得了BaseMock的所有配置和属性,只需要专注于自己的DerivedProperty配置即可,代码最简洁。
方案3:组合代替继承(备选)
如果上面两种方案不适合你的场景,还可以用组合的方式:让DerivedMock包含一个BaseMock实例,然后把Base的属性委托给它,同时自己处理Derived的配置:
class DerivedMock : Mock<IDerived> { private readonly BaseMock _baseMock; public int BaseProperty { get => _baseMock.BaseProperty; set => _baseMock.BaseProperty = value; } public int DerivedProperty { get; set; } = 73; public DerivedMock() { _baseMock = new BaseMock(); // 复用BaseMock的Setup逻辑 SetupGet(o => o.BaseProperty).Returns(BaseProperty); SetupGet(o => o.DerivedProperty).Returns(DerivedProperty); } }
不过这个方案需要手动委托属性,不如前两个方案高效,适合一些特殊场景。
我个人更推荐方案2,它既保持了代码的复用性,又让Mock类的结构更清晰,完全解决了你之前的编译问题和重复代码问题。
内容的提问来源于stack exchange,提问作者CzBuCHi
相关产品推荐
相关产品推荐

