.NET 3.1升级至6后Moq单元测试抛出System.InvalidProgramException异常
.NET 6升级后Moq Mock配合StreamWriter抛出异常的解决方法
问题描述
将项目从.NET Core 3.1升级到.NET 6后,如下Moq单元测试抛出System.InvalidProgramException : Cannot create boxed ByRef-like values异常:
public void StreamWriter_Test() { var stream = new Mock<Stream>(); stream.Setup(s => s.CanWrite).Returns(true); using var sw = new StreamWriter(stream.Object); sw.WriteLine("Test"); sw.Flush(); }
异常堆栈跟踪:
StreamProxy.Write(ReadOnlySpan`1 buffer)
StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
StreamWriter.Flush()
注:移除sw.WriteLine("Test");后测试可正常执行。
原因分析
.NET 6中StreamWriter的内部实现发生变更,会调用Stream的Write(ReadOnlySpan<byte>)方法。而ReadOnlySpan<T>属于by-ref-like类型,这类类型无法被装箱,Moq在生成Stream的动态代理类时,无法正确处理该方法的调用逻辑,从而触发异常。
解决方案
方案一:使用真实MemoryStream替代Mock
这是最简洁可靠的方案,测试StreamWriter的行为时,真实的MemoryStream既可以完成功能测试,还能直接验证写入内容:
public void StreamWriter_Test() { using var stream = new MemoryStream(); using var sw = new StreamWriter(stream); sw.WriteLine("Test"); sw.Flush(); // 验证写入内容(可选) stream.Position = 0; using var reader = new StreamReader(stream); Assert.Equal("Test", reader.ReadLine()); }
方案二:手动Setup Stream的Write(ReadOnlySpan)方法
如果业务场景必须使用Moq,可以手动Setup该Span重载的Write方法,避免Moq生成有问题的代理代码:
public void StreamWriter_Test() { var stream = new Mock<Stream>(); stream.Setup(s => s.CanWrite).Returns(true); // 显式Setup ReadOnlySpan<byte>版本的Write方法 stream.Setup(s => s.Write(It.IsAny<ReadOnlySpan<byte>>())) .Callback((ReadOnlySpan<byte> buffer) => { // 可在此添加自定义逻辑,比如记录写入的字节内容 }); using var sw = new StreamWriter(stream.Object); sw.WriteLine("Test"); sw.Flush(); // 验证Write方法被调用(可选) stream.Verify(s => s.Write(It.IsAny<ReadOnlySpan<byte>>()), Times.AtLeastOnce); }
另外建议确保Moq版本升级至最新(如4.18及以上),新版本对ref-like类型的支持有优化,但上述两种方案依然是更稳妥的选择。
内容的提问来源于stack exchange,提问作者Rohit
相关产品推荐
相关产品推荐

