VS2022中MS Test单元测试调试正常但常规运行失败排查
在VS2022使用MS Test进行单元测试时,遇到如下问题:使用Assert.AreEqual比较两个字符串值时,调试模式下执行正常,但常规运行测试时断言失败。原以为.NET原生字符串比较无需重写Equals方法,但调试与运行结果不一致,求原因分析。
测试代码
<TestMethod()> Public Sub ShouldReturnaPIItemOfMWEqualTo1_ReadyForHttpSend() Try Dim da = New LVDataLoader Dim ana As New Analogue Dim anas As New Analogues Dim inp As New LVItem Dim expected As String = "0.0012" Dim result As String = "" inp.TransformerId = "1" inp.InstrumentId = "1" inp.SubjectAssetId = "1" inp.SubjectAssetName = "L1 section of Feeder: 1" inp.EngUnit = "MW L1" inp.Value = "1201.1999499999999" result = da.GetPIItem(inp).Value.ToString Assert.AreEqual(expected, result) Catch ex As Exception Throw End Try End Sub
被调用的LVDataLoader.GetPIItem方法
Public Function GetPIItem(ByVal lvItem As LVItem) As PIItem Dim piitem As New PIItem Dim licArea As String Dim tagName As String Dim typeCode As String Dim ana As New Analogue Dim clnEngUnit As String Try 'First determine the license area for this item we do so by looking up the transformer id in the substation list licArea = Right(GetSubstation(lvItem.TransformerId).LicenceArea, 1) 'Determine the typecode for the engineering unit in question 'by comparing the engineering unit passed to the engineering unit property 'in the list of digitals. If IsDigital(lvItem.EngUnit, Digitals) Then typeCode = _digitalTypeCode Else typeCode = _analogueTypeCode End If 'Now build the tag name clnEngUnit = mutl.GetNthElement(lvItem.EngUnit, " ", 1) tagName = licArea & "~" & _voltage & "~" & lvItem.TransformerId & "~" & lvItem.InstrumentId & "-" & lvItem.SubjectAssetId & "~" & lvItem.SubjectAssetName & "~" & lvItem.EngUnit & "~" & typeCode piitem.TagAttribute = _tagAttribute piitem.TagRef = tagName piitem.TagName = tagName piitem.TimeStamp = lvItem.Time piitem.Quality = "192" piitem.Value = Math.Round(ana.Convert(clnEngUnit, lvItem.Value, _analogues), 5) piitem.ServerName = _destDatabase Return piitem Catch ex As Exception Throw End Try End Function
原因分析
这种调试与运行结果不一致的问题,核心原因并非字符串Equals方法的问题,而是浮点数值转换为字符串时的格式/精度差异,具体可能有以下几点:
浮点精度与ToString默认格式差异:
piitem.Value是Math.Round处理后的Double类型,调用无参数ToString()时,调试模式(JIT优化关闭)和运行模式(JIT优化开启)下,.NET对浮点数的字符串输出可能存在细微差别。比如调试时可能显示更贴合视觉的简化格式,而运行时可能因优化保留了更多隐含精度,导致ToString结果包含多余小数位(例如实际是0.0012000000001,ToString后变成"0.0012000000001",和预期的"0.0012"不匹配)。文化区域差异:
ToString()默认使用当前线程的文化设置,调试时你的开发环境可能是英文文化(小数点为.),但测试运行时可能使用了其他文化(比如某些欧洲文化用,作为小数点分隔符),导致字符串格式直接不匹配。JIT优化导致的计算差异:调试模式下JIT优化被禁用,浮点数计算的中间步骤会保留更多精度;而运行时开启JIT优化后,编译器可能对浮点运算进行优化,导致最终结果出现微小精度偏差,转换为字符串后就会触发断言失败。
解决方案
针对这类问题,推荐以下几种解决方式:
直接比较数值而非字符串
避免将浮点数值转换为字符串再比较,直接比较数值本身,并指定允许的误差范围(浮点数本身存在精度限制,不能直接用绝对相等):Dim expectedValue As Double = 0.0012 Dim resultValue As Double = Convert.ToDouble(da.GetPIItem(inp).Value) Assert.AreEqual(expectedValue, resultValue, 0.00001) ' 允许0.00001的误差固定ToString的输出格式
在转换字符串时显式指定格式,确保调试和运行时输出一致:result = da.GetPIItem(inp).Value.ToString("F4") ' 强制保留4位小数统一测试的文化区域
在测试方法开头设置固定的文化,避免因环境文化差异导致格式问题:System.Threading.Thread.CurrentThread.CurrentCulture = New System.Globalization.CultureInfo("en-US") System.Threading.Thread.CurrentThread.CurrentUICulture = New System.Globalization.CultureInfo("en-US")
内容的提问来源于stack exchange,提问作者Paul Johnson

