如何使用Moq模拟方法抛出NpgsqlException?该异常无可用构造函数
解决Moq模拟抛出NpgsqlException的问题
你遇到的情况很常见——NpgsqlException的构造函数确实是internal(内部)的,所以直接new实例或者普通反射查找构造函数都会失败。下面给你几个可行的解决方案,按推荐程度排序:
方案1:使用Npgsql官方提供的静态创建方法(推荐)
从Npgsql 6.0+版本开始,官方特意提供了NpgsqlException.Create静态方法,专门用来创建异常实例,完全不需要反射,代码干净又安全:
// 先构造PostgresErrorResponse来模拟具体的错误信息 var errorResponse = new PostgresErrorResponse { MessageText = "模拟的PostgreSQL错误", SqlState = "42P01" // 可以指定具体的PostgreSQL错误码,比如表不存在的错误码 }; // 创建NpgsqlException实例 var npgsqlException = NpgsqlException.Create(errorResponse); // 在Moq中配置抛出该异常 _someMock.Setup(x => x.SomeMethod(It.IsAny<int>())) .Throws(npgsqlException);
这个方法是官方设计的,不会因为Npgsql版本更新而失效,优先选这个。
方案2:用Activator.CreateInstance访问内部构造函数
如果你的Npgsql版本比较旧,没有Create方法,可以用Activator.CreateInstance配合绑定标志,强制访问内部构造函数。NpgsqlException通常有一个接受string message和Exception innerException的内部构造函数:
using System.Reflection; using System.Globalization; // 创建异常实例 var npgsqlException = (NpgsqlException)Activator.CreateInstance( typeof(NpgsqlException), BindingFlags.Instance | BindingFlags.NonPublic, // 指定访问非公共实例构造函数 null, new object[] { "模拟的Npgsql异常", null }, // 构造函数参数:错误消息+内部异常 CultureInfo.InvariantCulture ); // Moq中配置抛出该异常 _someMock.Setup(x => x.SomeMethod(It.IsAny<int>())) .Throws(npgsqlException);
如果上面的参数不对,可以用反射列出所有非公共构造函数来确认参数类型:
var ctors = typeof(NpgsqlException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic); foreach (var ctor in ctors) { Console.WriteLine($"构造函数参数:{string.Join(", ", ctor.GetParameters().Select(p => p.ParameterType.Name))}"); }
方案3:创建未初始化的实例(兜底方案)
如果上面两种方式都不行,可以用FormatterServices.GetUninitializedObject创建一个完全未初始化的实例,再通过反射设置必要的属性(比如错误消息):
using System.Runtime.Serialization; // 创建未初始化的异常实例 var npgsqlException = (NpgsqlException)FormatterServices.GetUninitializedObject(typeof(NpgsqlException)); // 反射设置Message属性(如果测试需要用到错误消息) var messageField = typeof(NpgsqlException).GetField("_message", BindingFlags.Instance | BindingFlags.NonPublic); messageField?.SetValue(npgsqlException, "模拟的Npgsql异常"); // Moq中配置抛出该异常 _someMock.Setup(x => x.SomeMethod(It.IsAny<int>())) .Throws(npgsqlException);
注意:这个方法创建的实例没有经过构造函数初始化,可能某些内部状态不正常,如果你的测试逻辑只需要判断异常类型,不需要访问异常的其他属性,这个方法是可行的。
内容的提问来源于stack exchange,提问作者Doctor
相关产品推荐
相关产品推荐

