如何为C# MVVC项目的Update方法编写单元测试
C# MVVM项目Update方法单元测试问题解决方案
现有代码的核心问题
现有Update方法无法直接编写单元测试的根本原因是代码与外部Oracle数据库强耦合,没有做依赖抽象,具体问题包括:
- 方法内部直接实例化数据库连接类
Conexion,无法替换为测试用的模拟连接 - 直接依赖Oracle专属实现类(
OracleConnection/OracleCommand),没有依赖.NET官方定义的数据库操作抽象接口 - 数据访问逻辑与业务模型绑定,违反单一职责原则,单元测试无法脱离真实数据库运行
改造方案
第一步:抽象数据库操作接口
定义通用的数据操作接口,隔离业务代码与具体数据库实现:
public interface IDbHelper { // 执行存储过程,返回受影响行数 int ExecuteStoredProcedure(string procName, Dictionary<string, object> parameters); }
第二步:实现Oracle专属的DbHelper
把原有数据库连接、执行命令的逻辑抽离到单独的实现类:
public class OracleDbHelper : IDbHelper { public int ExecuteStoredProcedure(string procName, Dictionary<string, object> parameters) { using var conn = new Conexion().abrirConexion(); using var cmd = new OracleCommand(procName, conn); cmd.CommandType = CommandType.StoredProcedure; foreach (var param in parameters) { cmd.Parameters.Add(param.Key, OracleDbType.NVarchar2).Value = param.Value; } var result = cmd.ExecuteNonQuery(); conn.Close(); return result; } }
第三步:改造原有Model类,通过依赖注入传入IDbHelper
public class UsuarioInterno { private readonly IDbHelper _dbHelper; // 构造函数注入依赖,单元测试时可传入模拟的IDbHelper实现 public UsuarioInterno(IDbHelper dbHelper) { _dbHelper = dbHelper; } public string Name { get; set; } public string Surname { get; set; } public bool Update() { try { var parameters = new Dictionary<string, object> { {"i_name", this.Name}, {"i_surname", this.Surname} }; _dbHelper.ExecuteStoredProcedure("u_usuario_interno", parameters); return true; } catch (Exception) { return false; } } }
单元测试编写示例
使用Mock框架(如Moq)模拟IDbHelper的行为,分别测试成功、异常场景:
using Moq; using Xunit; public class UsuarioInternoTests { [Fact] public void Update_WhenExecuteSuccess_ReturnsTrue() { // 模拟IDbHelper执行成功的情况 var mockDbHelper = new Mock<IDbHelper>(); mockDbHelper.Setup(d => d.ExecuteStoredProcedure(It.IsAny<string>(), It.IsAny<Dictionary<string, object>>())) .Returns(1); var usuario = new UsuarioInterno(mockDbHelper.Object) { Name = "张三", Surname = "李四" }; var result = usuario.Update(); Assert.True(result); // 验证参数传递是否正确 mockDbHelper.Verify(d => d.ExecuteStoredProcedure("u_usuario_interno", It.Is<Dictionary<string, object>>(p => (string)p["i_name"] == "张三" && (string)p["i_surname"] == "李四")), Times.Once); } [Fact] public void Update_WhenThrowException_ReturnsFalse() { // 模拟IDbHelper执行抛出异常的情况 var mockDbHelper = new Mock<IDbHelper>(); mockDbHelper.Setup(d => d.ExecuteStoredProcedure(It.IsAny<string>(), It.IsAny<Dictionary<string, object>>())) .Throws(new Exception("数据库执行错误")); var usuario = new UsuarioInterno(mockDbHelper.Object); var result = usuario.Update(); Assert.False(result); } }
其他注意事项
- 如果不想改动原有代码结构,也可以使用连接真实测试Oracle库的方式做集成测试,但这种测试不属于单元测试范畴,执行速度慢且依赖环境
- 原有代码中
conn的实例化可以优化,推荐用using语句自动释放连接,不需要手动调用Close方法,避免异常时资源泄漏
内容的提问来源于stack exchange,提问作者AndreaP
相关产品推荐
相关产品推荐

