如何Mock NpgsqlTransaction与NpgsqlConnection用于单元测试
Mock NpgsqlTransaction的可行方案
以下是从低成本到全可控的三种实现方案,可根据你的业务场景选择:
方案1:使用官方抽象类(推荐,改造成本最低)
适用场景:你的SaveSchema方法中没有调用NpgsqlTransaction特有的API,只用了Commit、Rollback等通用事务方法
- .NET 官方提供了数据库操作的抽象层
DbTransaction,NpgsqlTransaction本身就是该抽象类的子类 - 直接修改
SaveSchema方法的参数类型即可,生产环境的调用逻辑不需要做任何改动:
// 改造后的方法签名 public bool SaveSchema(Schema newSchema, DbTransaction transaction)
- 单元测试时直接用常用Mock框架(Moq、NSubstitute等)Mock
DbTransaction抽象类即可,不需要额外编写任何包装代码
方案2:完善适配器模式(适用需要调用Npgsql特有API的场景)
你之前的适配器思路是正确的,只是没有把抽象和实现解耦完全,调整后即可使用:
- 先补充接口定义,把你业务代码中用到的所有事务相关方法都声明在接口中:
public interface INpgsqlTransactionAdapter { void Commit(); void Rollback(); // 其他你用到的NpgsqlTransaction特有方法,按需添加 } public interface INpgsqlConnectionAdapter { INpgsqlTransactionAdapter BeginTransaction(); void Commit(); }
- 调整适配器实现,把生产逻辑转发给真实实例:
public class NpgsqlConnectionAdapter : INpgsqlConnectionAdapter { private readonly NpgsqlConnection _npgsqlConnection; public NpgsqlConnectionAdapter(string connectionString) { _npgsqlConnection = new NpgsqlConnection(connectionString); } public INpgsqlTransactionAdapter BeginTransaction() { return new NpgsqlTransactionAdapter(_npgsqlConnection.BeginTransaction()); } public void Commit() => _npgsqlConnection.Close(); } public class NpgsqlTransactionAdapter : INpgsqlTransactionAdapter { private readonly NpgsqlTransaction _npgsqlTransaction; public NpgsqlTransactionAdapter(NpgsqlTransaction npgsqlTransaction) { _npgsqlTransaction = npgsqlTransaction; } public void Commit() => _npgsqlTransaction.Commit(); public void Rollback() => _npgsqlTransaction.Rollback(); }
- 把
SaveSchema的参数类型改成INpgsqlTransactionAdapter,单元测试时直接Mock该接口即可,不需要关心NpgsqlTransaction的构造逻辑
方案3:不改现有业务代码的兼容方案
如果不想调整现有生产代码的签名,可以用Mock框架的私有构造函数支持,以Moq为例:
// 直接Mock NpgsqlTransaction的父类DbTransaction,使用时强转即可 var mockTrans = new Mock<DbTransaction>(); // 配置你需要的方法行为 mockTrans.Setup(t => t.Commit()).Callback(() => { /* 自定义逻辑 */ }); // 调用SaveSchema时强转参数 SaveSchema(testSchema, (NpgsqlTransaction)mockTrans.Object);
注意:该方案仅适用于你的业务代码没有调用NpgsqlTransaction特有API的场景,否则运行时会抛出类型转换异常
内容的提问来源于stack exchange,提问作者I am not Fat
相关产品推荐
相关产品推荐

