使用MSTest测试F#代码时无法解析Assert.AreEqual方法重载
在学习用MSTest测试F#代码时,遇到了Assert.AreEqual的重载解析问题:仅传入bool类型的预期值和实际值时编译正常,但添加消息字符串后,无论添加类型注解还是使用命名参数,都无法让编译器识别正确的重载,只能用Assert.Fail作为临时替代,但始终没搞懂Assert.AreEqual的正确调用方式。
问题代码示例
[<TestClass>] type MyTests () = [<TestMethod>] member this.SyntaxTest () = Assert.AreEqual<bool>(true, false) // 编译通过 Assert.AreEqual<bool>(expected=true, actual=false) // 编译通过 Assert.AreEqual<bool>((true : bool), (false : bool)) // 编译通过 Assert.AreEqual<bool>(true, false, "message string") // 编译失败 Assert.AreEqual<bool>(true, false, ("message string" : string)) // 编译失败 Assert.AreEqual<bool>(expected=true, actual=false, message="message string") // 编译失败 // 可行替代方案,但未解决Assert.AreEqual的调用问题: if true <> false then Assert.Fail("Fail message")
报错信息
FS0041: 无法基于此程序点之前的类型信息确定方法 'AreEqual' 的唯一重载。可能需要类型注解。已知参数类型:bool * bool * string 候选重载:- Assert.AreEqual<'T>(expected: 'T | null, actual: 'T | null, message: string | null) : unit - Assert.AreEqual<'T>(expected: 'T | null, actual: 'T | null, message: string | null, [] parameters: obj | null array | null) : unit ....
解决方法
核心原因:F#编译器无法区分三参数的Assert.AreEqual<'T>(T, T, string)和带params数组的四参数重载Assert.AreEqual<'T>(T, T, string, obj[])——因为.NET的params数组参数是可选的,编译器会把三参数调用同时匹配这两个重载,导致歧义。
有几种直接的解决方式:
显式指定方法签名:通过类型注解明确告知编译器要调用三参数的重载:
(Assert.AreEqual<bool> : bool -> bool -> string -> unit)(true, false, "message string")传入null作为params数组参数:主动匹配四参数重载,最后一个参数传null(无需格式化参数时可用):
Assert.AreEqual<bool>(true, false, "message string", null)使用命名参数并明确params参数:结合命名参数和null值,锁定目标重载:
Assert.AreEqual<bool>(expected = true, actual = false, message = "message string", parameters = null)封装包装函数:创建一个专用的包装函数,避免每次都处理重载问题:
let assertEqualWithMsg (expected: 'T) (actual: 'T) (msg: string) = Assert.AreEqual<'T>(expected, actual, msg) // 调用示例 assertEqualWithMsg true false "message string"
内容的提问来源于stack exchange,提问作者John Christopher Linstrum

