SSH.Net.Sftp不可重写属性无法模拟的单元测试问题
问题:无法模拟SSH.Net的SftpFileAttributes类进行单元测试
业务场景
单元测试时需要跳过部分代码逻辑,直接走到return IsFile;分支。其中client是基于SSH.Net SftpClient封装的可模拟类,SftpCheckPathExistence为自定义枚举。
业务代码
SftpFileAttributes attrs = client.GetAttributes(fileName); if (attrs.IsDirectory) { return SftpCheckPathExistence.IsDirectory; } return SftpCheckPathExistence.IsFile;
单元测试卡点
测试代码中需要模拟GetAttributes方法的返回值,但SftpFileAttributes类无法直接处理:
clientMock.Setup(f => f.GetAttributes(It.IsAny<string>())).Returns(???);
已尝试的方法及问题
尝试1:不设置Mock
SftpFileAttributes attrs = client.GetAttributes(fileName); // attrs为null if (attrs.IsDirectory) // 触发空引用异常问题:未Mock的方法返回null,导致空引用报错。
尝试2:直接实例化SftpFileAttributes
var sftpFileAttributes = new SftpFileAttributes(); // 无公共构造函数,报错 sftpFileAttributes.IsDirectory = false; // 属性为私有setter,无法赋值 clientMock.Setup(f => f.GetAttributes(It.IsAny<string>())).Returns(sftpFileAttributes);问题:类无公共构造函数,且
IsDirectory属性无法直接修改。尝试3:用Moq模拟SftpFileAttributes
var sftpFileAttributesMock = new Mock<SftpFileAttributes>(); sftpFileAttributesMock.SetupGet(f => f.IsDirectory).Returns(false); // 报错:IsDirectory未标记为virtual,无法被Moq重写 clientMock.Setup(f => f.GetAttributes(It.IsAny<string>())).Returns(sftpFileAttributes.Object);错误信息:
System.NotSupportedException HResult=0x80131515 Message=Unsupported expression: f => f.IsDirectory Non-overridable members (here: SftpFileAttributes.get_IsDirectory) may not be used in setup / verification expressions. Source=Moq StackTrace: at Moq.Guard.IsOverridable(MethodInfo method, Expression expression)问题:Moq仅支持模拟可重写(virtual)成员,而
IsDirectory不满足该条件。
解决方案
方案1:通过反射创建并修改实例
利用反射绕过无公共构造函数和私有setter的限制:
// 反射获取非公共构造函数 var constructor = typeof(SftpFileAttributes).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null); var attrs = (SftpFileAttributes)constructor.Invoke(null); // 反射修改IsDirectory的私有setter typeof(SftpFileAttributes).GetProperty("IsDirectory") .SetValue(attrs, false); // 设置Mock返回值 clientMock.Setup(f => f.GetAttributes(It.IsAny<string>())).Returns(attrs);
方案2:封装自定义DTO避开SSH.Net类限制
在封装SftpClient时,将GetAttributes的返回值转换为自定义数据对象,彻底隔离第三方类:
// 自定义DTO public class SftpItemInfo { public bool IsDirectory { get; set; } // 按需添加其他属性 } // 封装的Client方法 public SftpItemInfo GetItemInfo(string fileName) { var attrs = _sftpClient.GetAttributes(fileName); return new SftpItemInfo { IsDirectory = attrs.IsDirectory }; }
此时单元测试可直接Mock返回自定义的SftpItemInfo,无需处理SSH.Net的类限制。
内容的提问来源于stack exchange,提问作者Mihai Socaciu
相关产品推荐
相关产品推荐

