C#中如何对连接SQL数据库的方法进行无真实库单元测试?
解决方案
你当前的代码直接在业务逻辑内部实例化SqlConnection,属于强耦合实现,无法直接用Moq替换,因为Moq仅支持Mock接口和虚方法。你可以通过依赖抽象的方式解耦数据库连接依赖,无需连接真实数据库即可完成测试,具体步骤如下:
1. 抽象数据库连接工厂
首先定义连接创建的抽象接口,隔离业务代码和具体数据库连接的实现逻辑:
public interface IDbConnectionFactory { IDbConnection CreateConnection(string connectionString); }
生产环境使用的真实实现:
public class SqlConnectionFactory : IDbConnectionFactory { public IDbConnection CreateConnection(string connectionString) { return new SqlConnection(connectionString); } }
2. 改造原有业务方法
将连接工厂通过构造函数注入到业务类中,替换原有直接new SqlConnection的逻辑:
public class YourBusinessClass { private readonly IDbConnectionFactory _connectionFactory; public YourBusinessClass(IDbConnectionFactory connectionFactory) { _connectionFactory = connectionFactory; } // 你要测试的原有业务方法 public bool YourTargetMethod(string connectionString, string query, object sqlParams, int commandTimeout, bool success) { using (var connection = _connectionFactory.CreateConnection(connectionString)) { try { connection.Execute(query, sqlParams, commandTimeout: commandTimeout); return success; } catch { // 你原有代码的异常处理逻辑 return false; } } } }
3. 编写单元测试用例
使用Moq MockIDbConnectionFactory和IDbConnection即可模拟数据库操作,不需要真实连接:
using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System.Data; using Dapper; [TestClass] public class YourBusinessClassTests { [TestMethod] public void YourTargetMethod_ExecuteSuccess_ReturnsExpectedSuccessValue() { // 准备测试参数 var testConnStr = "Test_Connection_String"; var testQuery = "UPDATE Test SET Value = @Id"; var testParams = new { Id = 1 }; var testTimeout = 30; var expectedResult = true; // Mock数据库连接 var mockConn = new Mock<IDbConnection>(); mockConn.Setup(c => c.Open()); mockConn.Setup(c => c.Close()); // Mock连接工厂,传入指定连接字符串时返回Mock的连接对象 var mockFactory = new Mock<IDbConnectionFactory>(); mockFactory.Setup(f => f.CreateConnection(testConnStr)).Returns(mockConn.Object); // 初始化被测业务类 var bizInstance = new YourBusinessClass(mockFactory.Object); // 执行测试方法 var actualResult = bizInstance.YourTargetMethod(testConnStr, testQuery, testParams, testTimeout, expectedResult); // 验证结果和调用逻辑 Assert.AreEqual(expectedResult, actualResult); // 验证工厂用传入的连接字符串创建了连接 mockFactory.Verify(f => f.CreateConnection(testConnStr), Times.Once); // 验证Execute方法参数符合预期 mockConn.Verify(c => c.Execute( It.Is<string>(q => q == testQuery), It.Is<object>(p => p == testParams), It.IsAny<IDbTransaction>(), It.Is<int>(t => t == testTimeout), It.IsAny<CommandType?>()), Times.Once); // 验证连接被正常释放 mockConn.Verify(c => c.Dispose(), Times.Once); } [TestMethod] public void YourTargetMethod_ExecuteThrowException_ReturnsFailure() { // 模拟数据库执行报错的场景 var mockConn = new Mock<IDbConnection>(); mockConn.Setup(c => c.Execute(It.IsAny<string>(), It.IsAny<object>(), null, null, null)) .Throws<System.Data.SqlClient.SqlException>(); var mockFactory = new Mock<IDbConnectionFactory>(); mockFactory.Setup(f => f.CreateConnection(It.IsAny<string>())).Returns(mockConn.Object); var bizInstance = new YourBusinessClass(mockFactory.Object); var result = bizInstance.YourTargetMethod("any_conn_str", "any_query", null, 30, true); Assert.IsFalse(result); } }
补充说明
- 如果你不想改动原有业务代码结构,也可以使用.NET Fakes、Pose这类库直接替换
SqlConnection的构造逻辑,但该方案属于黑盒测试,稳定性和可维护性不如依赖注入方案,优先推荐使用抽象工厂的改造方式。 - Dapper的
Execute是扩展方法,底层会调用IDbConnection的原生方法,因此只要Mock好IDbConnection的对应逻辑就可以完成全场景测试。
内容的提问来源于stack exchange,提问作者ZeldaZonk
相关产品推荐
相关产品推荐

