You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#单元测试中Mock SqlConnection未生效仍创建真实连接问题

问题原因

你当前的Mock代码完全不生效,核心有两个硬伤:

  • ExecuteGetObj方法内部直接通过new SqlConnection(...)硬编码创建真实数据库连接实例,和你在测试里构造的Mock<IDbConnection>对象没有任何关联,方法执行时根本不会获取你构造的Mock实例,自然会走真实的数据库连接逻辑。
  • 方法内的SqlCommand也是直接通过new SqlCommand(query, connection)创建的,没有调用connection.CreateCommand()方法,你之前写的connection.Setup(m => m.CreateCommand())逻辑完全没有触发入口。

注意:Moq这类基于动态代理的Mock框架,只能Mock接口、虚方法/抽象方法,根本无法拦截非虚的具体类实例化、静态方法调用,直接硬编码在方法内部的new操作,靠常规Moq写法是不可能替换成Mock对象的。

推荐解决方案:重构代码实现依赖倒置

这是可维护性最高、符合单元测试最佳实践的方案,不需要依赖特殊的Mock框架,也可以兼容原有方法的静态属性。

  1. 抽象数据库连接创建逻辑,不要在业务方法内直接new连接对象
/// <summary>
/// 连接工厂抽象,隔离连接创建逻辑
/// </summary>
public interface IDbConnectionFactory
{
    IDbConnection CreateConnection();
}

/// <summary>
/// 生产环境用的真实连接工厂实现
/// </summary>
public class SqlConnectionFactory : IDbConnectionFactory
{
    private readonly string _connStr;
    public SqlConnectionFactory(string connectionString)
    {
        _connStr = connectionString;
    }
    public IDbConnection CreateConnection()
    {
        return new SqlConnection(_connStr);
    }
}
  1. 改造原ExecuteGetObj方法,将依赖作为参数传入,同时调整Command创建逻辑,不要直接硬编码实例化SqlCommand
public static object ExecuteGetObj(string query, IDbConnectionFactory connectionFactory)
{
    object obj = null;

    using (var connection = connectionFactory.CreateConnection())
    {
        connection.Open();
        // 改用连接对象的CreateCommand方法创建命令,方便Mock替换
        using (IDbCommand cmd = connection.CreateCommand())
        {
            cmd.CommandText = query;
            obj = cmd.ExecuteScalar();
        }
    }

    return obj;
}
  1. 调整单元测试代码,通过Mock工厂注入你构造的Mock连接、Mock命令
// 构造Mock命令
var command = new Mock<IDbCommand>();
command.Setup(x => x.ExecuteScalar())
       .Returns(null) // 显式指定返回值匹配断言预期
       .Verifiable();

// 构造Mock连接
var connection = new Mock<IDbConnection>();
connection.Setup(m => m.Open()); // 配置Open方法行为,默认不抛错即可
connection.Setup(m => m.CreateCommand())
          .Returns(command.Object);

// 构造Mock连接工厂
var mockFactory = new Mock<IDbConnectionFactory>();
mockFactory.Setup(f => f.CreateConnection())
           .Returns(connection.Object);

// 执行测试方法,注入Mock依赖
var obj = Common.ExecuteGetObj("select 1", mockFactory.Object);

// 断言
Assert.IsNull(obj);
command.Verify(x => x.ExecuteScalar(), Times.Once); // 验证方法确实按预期执行

生产环境调用该方法时,传入真实的SqlConnectionFactory实例即可,不会影响原有业务逻辑。

备选方案:使用支持实例化拦截的Mock框架

如果你暂时无法重构生产代码,可以使用支持拦截非虚方法、new实例化操作的Mock框架,比如JustMock、Microsoft Fakes、Pose等,直接在测试中拦截new SqlConnection、new SqlCommand的操作,返回你构造的Mock对象。但这类方案要么是商业付费框架,要么配置复杂、和运行环境绑定紧,长期可维护性远不如重构依赖的方案,不推荐优先使用。

内容的提问来源于stack exchange,提问作者Hary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 23:18:33