Moq模拟方法未返回预设值却执行真实方法致测试崩溃求助
问题根源
你的Mock不生效的核心原因是:ExpectVariableValue.ExecuteAction方法内部直接硬编码实例化了GetVariable对象,完全没有用到你在测试中创建的Mock<IGetVariable>实例。程序执行时会跳过Mock,直接创建真实的GetVariable对象并调用其真实方法,自然会因为connection为null而崩溃。
解决办法
要让Mock生效,必须重构代码,将IGetVariable的创建逻辑从ExpectVariableValue内部移出,通过依赖注入的方式传入,这样测试时才能用Mock替换真实对象。以下提供两种可行方案:
方案一:直接注入IGetVariable实例(适合变量名固定的场景)
1. 重构ExpectVariableValue类
添加支持依赖注入的构造函数,移除内部硬编码的new GetVariable:
public class ExpectVariableValue { private readonly string _expectedValue; private readonly IGetVariable _getVariable; // 生产环境构造函数(保留原有调用方式) public ExpectVariableValue(string variableName, string expectedValue) : this(new GetVariable(variableName), expectedValue) { } // 测试专用构造函数(注入Mock实例) public ExpectVariableValue(IGetVariable getVariable, string expectedValue) { _getVariable = getVariable; _expectedValue = expectedValue; } public virtual ActionResult ExecuteAction(object sender, SimConnect connection) { // 直接使用注入的IGetVariable实例,不再自己创建 var result = _getVariable.ExecuteAction(sender, connection); string isExpectedValue = CheckIfVariableHasExpectedValue(sender, connection, result.ComputedResult); return new ActionResult(isExpectedValue, isExpectedValue, result.Error); } // 原有CheckIfVariableHasExpectedValue方法... }
2. 修改测试代码
直接将Mock的IGetVariable实例传入被测对象:
[TestClass] public class ExpectVariableValueTests { ExpectVariableValue expectedVariableValue; Mock<IGetVariable> getVariableMock; [TestMethod] public void NumericValue_VariableHasTargetValue_TrueIsReturned() { // 配置Mock的返回值 getVariableMock = new Mock<IGetVariable>(); getVariableMock.Setup(x => x.ExecuteAction(It.IsAny<object>(), It.IsAny<SimConnect>())) .Returns(new ActionResult(null, "MyATCID", false)); // 使用测试构造函数,传入Mock实例 this.expectedVariableValue = new ExpectVariableValue(getVariableMock.Object, "MyATCID"); var result = this.expectedVariableValue.ExecuteAction(null, null); result.Should().Be("True"); } }
方案二:使用工厂模式注入(适合需要动态创建IGetVariable的场景)
如果ExpectVariableValue需要根据不同的变量名创建IGetVariable,可以用工厂模式解耦:
1. 定义工厂接口与实现
// 工厂接口 public interface IGetVariableFactory { IGetVariable Create(string variableName); } // 生产环境工厂实现 public class GetVariableFactory : IGetVariableFactory { public IGetVariable Create(string variableName) { return new GetVariable(variableName); } }
2. 重构ExpectVariableValue类
注入工厂接口,通过工厂创建IGetVariable实例:
public class ExpectVariableValue { private readonly string _variableName; private readonly string _expectedValue; private readonly IGetVariableFactory _factory; // 生产环境构造函数 public ExpectVariableValue(string variableName, string expectedValue) : this(variableName, expectedValue, new GetVariableFactory()) { } // 测试专用构造函数 public ExpectVariableValue(string variableName, string expectedValue, IGetVariableFactory factory) { _variableName = variableName; _expectedValue = expectedValue; _factory = factory; } public virtual ActionResult ExecuteAction(object sender, SimConnect connection) { // 通过工厂创建IGetVariable实例,不再硬编码 IGetVariable gv = _factory.Create(_variableName); var result = gv.ExecuteAction(sender, connection); string isExpectedValue = CheckIfVariableHasExpectedValue(sender, connection, result.ComputedResult); return new ActionResult(isExpectedValue, isExpectedValue, result.Error); } // 原有CheckIfVariableHasExpectedValue方法... }
3. 修改测试代码
Mock工厂,让它返回预设的IGetVariable实例:
[TestClass] public class ExpectVariableValueTests { ExpectVariableValue expectedVariableValue; Mock<IGetVariable> getVariableMock; Mock<IGetVariableFactory> factoryMock; [TestMethod] public void NumericValue_VariableHasTargetValue_TrueIsReturned() { // 配置IGetVariable的Mock返回值 getVariableMock = new Mock<IGetVariable>(); getVariableMock.Setup(x => x.ExecuteAction(It.IsAny<object>(), It.IsAny<SimConnect>())) .Returns(new ActionResult(null, "MyATCID", false)); // 配置工厂Mock,让它返回上面的IGetVariable实例 factoryMock = new Mock<IGetVariableFactory>(); factoryMock.Setup(f => f.Create("ATC ID")) .Returns(getVariableMock.Object); // 注入工厂Mock创建被测对象 this.expectedVariableValue = new ExpectVariableValue("ATC ID", "MyATCID", factoryMock.Object); var result = this.expectedVariableValue.ExecuteAction(null, null); result.Should().Be("True"); } }
总结
两种方案的核心都是解除ExpectVariableValue对GetVariable的直接依赖,让依赖对象可以从外部传入,这样测试时就能用Moq生成的Mock实例替换真实对象,达到隔离外部依赖、验证逻辑的目的。
内容的提问来源于stack exchange,提问作者Albert FG
相关产品推荐
相关产品推荐

