如何用Moq模拟基类虚方法并测试子类重写方法
如何测试子类重写方法的验证逻辑,同时模拟基类的数据库操作?
你遇到的问题根源在于:直接Mock子类的GetTitle方法会完全覆盖你要测试的重写逻辑——Moq会直接返回你Setup的结果,根本不会执行Foo类里的if (id <0)验证,也不会调用base.GetTitle。
要解决这个问题,我们需要用部分Mock(Partial Mock):让子类的重写逻辑正常执行,只替换基类中涉及数据库的GetTitle方法。下面是具体的实现方案:
修正后的测试代码
public class FooTests { [Fact] public void GetTitle_WhenIdIsValid_ReturnsFormattedTitleWithMockedBaseValue() { // Arrange int validId = 1; string mockedBaseTitle = "Mocked DB Title"; // 创建Partial Mock,CallBase=true表示未被Setup的方法会执行真实逻辑 var fooMock = new Mock<Foo>() { CallBase = true }; // 专门模拟基类的GetTitle方法(通过强制类型转换指定调用基类版本) fooMock.Setup(x => ((FooBase)x).GetTitle(It.IsAny<int>())).Returns(mockedBaseTitle); var fooInstance = fooMock.Object; // Act string result = fooInstance.GetTitle(validId); // Assert // 验证基类方法被正确调用了一次,参数为validId fooMock.Verify(x => ((FooBase)x).GetTitle(validId), Times.Once); // 验证返回结果符合子类的格式化逻辑 Assert.Equal($"Valid ID ({mockedBaseTitle})", result); } [Fact] public void GetTitle_WhenIdIsInvalid_ReturnsErrorMessage() { // Arrange int invalidId = -1; // 这里不需要Mock,因为无效ID的逻辑不会调用基类方法 var fooInstance = new Foo(); // Act string result = fooInstance.GetTitle(invalidId); // Assert Assert.Equal("Invalid id", result); } }
关键细节说明
CallBase = true的作用:
这个配置告诉Moq:对于没有被Setup的方法(也就是Foo类重写的GetTitle),执行真实的代码逻辑,而不是返回默认的空值或Mock结果。这样我们就能测试到子类的验证逻辑。模拟基类方法的正确姿势:
通过((FooBase)x).GetTitle(...)的方式,明确告诉Moq我们要替换的是基类的virtual方法,而不是子类的重写版本。这样当子类的GetTitle调用base.GetTitle(id)时,就会执行我们Mock的逻辑,而不是真实的数据库操作。拆分测试用例:
- 有效ID的测试需要Mock基类,验证子类的格式化逻辑和基类调用情况;
- 无效ID的测试不需要Mock,直接用真实的
Foo对象即可,因为这部分逻辑完全不依赖基类的数据库操作。
原来的测试失败,就是因为你直接Mock了子类的GetTitle,跳过了所有你想测试的重写逻辑——现在调整后,就能精准测试子类的验证逻辑,同时隔离基类的数据库依赖了。
内容的提问来源于stack exchange,提问作者Fred
相关产品推荐
相关产品推荐

