如何模拟exe签名信息?签名验证测试与流转换问题
1. 如何将fileSystem.File.OpenRead()返回的Stream转换为FileStream?
System.IO.Abstractions的MockFile.OpenRead()返回的是MemoryStream,和FileStream属于不同类型,所以用as FileStream转换会返回null。解决这个问题有几种实用思路:
思路一:修改方法依赖,兼容任意Stream
如果FileSignatureInfo.GetFromFileStream()的参数可以改为接受Stream(而非仅FileStream),直接去掉类型转换即可:
public static bool IsSigned(IFileSystem fileSystem, string filePath) { using var stream = fileSystem.File.OpenRead(filePath); var signatureInfo = FileSignatureInfo.GetFromFileStream(stream); return signatureInfo.State == SignatureState.SignedAndTrusted; }
如果这个方法是第三方库提供的、无法修改参数,就换下面的思路。
思路二:用真实临时文件替代模拟文件
放弃MockFileSystem,在测试中创建真实的临时无签名exe文件,测试完成后自动清理:
[Test] public void IsSignedReturnsFalseForUnsignedFile() { // Arrange var tempFilePath = Path.Combine(Path.GetTempPath(), "test_unsigned.exe"); File.WriteAllBytes(tempFilePath, new byte[100]); // 创建空的临时exe文件 try { // Act var result = MyHelper.IsSigned(new FileSystem(), tempFilePath); // Assert Assert.IsFalse(result); } finally { File.Delete(tempFilePath); // 清理临时文件 } }
这种方式能拿到真实的FileStream,保证流程正常执行。
思路三:自定义Mock返回FileStream
如果一定要用MockFileSystem,可以继承它并重写OpenRead方法,返回真实的FileStream(本质还是依赖真实文件):
public class CustomMockFileSystem : MockFileSystem { public override Stream OpenRead(string path) { // 确保path指向预先准备好的真实文件 return new FileStream(path, FileMode.Open, FileAccess.Read); } }
但这种方式需要提前准备真实文件,不如临时文件方案直接。
2. 单元测试证书签名验证的最佳方案是什么?
单元测试的核心是测试你的业务逻辑,而非依赖的第三方/系统API。FileSignatureInfo.GetFromFileStream()属于系统或第三方提供的签名验证逻辑,这部分不需要你去测试——你要验证的是:当签名状态为SignedAndTrusted时返回true,其他状态返回false。
最佳实践是抽象依赖,模拟签名验证结果,具体步骤如下:
步骤1:抽象签名验证逻辑
定义接口封装签名验证的调用,隔离对第三方API的依赖:
public interface ISignatureChecker { SignatureState GetSignatureState(Stream stream); } public class FileSignatureChecker : ISignatureChecker { public SignatureState GetSignatureState(Stream stream) { var signatureInfo = FileSignatureInfo.GetFromFileStream(stream as FileStream); return signatureInfo.State; } }
步骤2:修改IsSigned方法,注入抽象接口
public static bool IsSigned(IFileSystem fileSystem, ISignatureChecker signatureChecker, string filePath) { using var stream = fileSystem.File.OpenRead(filePath); var state = signatureChecker.GetSignatureState(stream); return state == SignatureState.SignedAndTrusted; }
步骤3:编写单元测试,模拟接口返回
用Moq等框架模拟ISignatureChecker的返回值,覆盖不同场景:
[Test] public void IsSigned_ReturnsFalse_WhenSignatureStateIsNotTrusted() { // Arrange var mockFileSystem = new MockFileSystem(); mockFileSystem.AddFile(@"C:\test\test.exe", new MockFileData("dummy content")); var mockChecker = new Mock<ISignatureChecker>(); mockChecker.Setup(c => c.GetSignatureState(It.IsAny<Stream>())).Returns(SignatureState.NotSigned); // Act var result = MyHelper.IsSigned(mockFileSystem, mockChecker.Object, @"C:\test\test.exe"); // Assert Assert.IsFalse(result); } [Test] public void IsSigned_ReturnsTrue_WhenSignatureStateIsTrusted() { // Arrange var mockFileSystem = new MockFileSystem(); mockFileSystem.AddFile(@"C:\test\test.exe", new MockFileData("dummy content")); var mockChecker = new Mock<ISignatureChecker>(); mockChecker.Setup(c => c.GetSignatureState(It.IsAny<Stream>())).Returns(SignatureState.SignedAndTrusted); // Act var result = MyHelper.IsSigned(mockFileSystem, mockChecker.Object, @"C:\test\test.exe"); // Assert Assert.IsTrue(result); }
补充:集成测试验证完整流程
如果要验证真实的签名验证逻辑(比如确保调用系统API的流程正确),可以做集成测试:准备几个真实的测试文件(无签名、有效签名、无效签名),用真实的FileSystem和FileSignatureChecker测试整个流程。
内容的提问来源于stack exchange,提问作者sr28

