As a learning exercise, I'm trying to test some F# code using MSTEST. I've run into a problem that is driving me crazy, not because I can't work around it, but because I don't understand how to properly add a type annotation to determine what overloaded method I'm using (meaning I haven't properly learned what I'm trying to learn).
Anyway, here is a simplified example of the non-working code:
[<TestClass>]
type MyTests () =
[<TestMethod>]
member this.SyntaxTest () =
Assert.AreEqual<bool>(true, false) // compiles
Assert.AreEqual<bool>(expected=true, actual=false) // compiles
Assert.AreEqual<bool>((true : bool), (false : bool)) // compiles
Assert.AreEqual<bool>(true, false, "message string") // fails to compile
Assert.AreEqual<bool>(true, false, ("message string" : string)) // fails to compile
Assert.AreEqual<bool>(expected=true, actual=false, message="message string") // fails to compile
// Simple work around that doesn't answer how to properly make the Assert.AreEqual call:
if (true <> false) then do
Assert.Fail("Fail message")
As can be seen in the comments, the version using only bool inputs works fine using several different versions of type and parameter notations. However, as soon as I add a message string, nothing I can think of to do will make it compile.
The fail code I get is:
FS0041: A unique overload for method 'AreEqual' could not be determined based on type information prior to this program point. A type annotation may be needed. Known types of arguments: bool * bool * string Candidates: - 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 ....
From the Microsoft documentation link there are an awful lot of different Assert.AreEqual methods with slightly different signatures, but I apparently have no idea how to tell the F# compiler which one I want. From what I've read, I thought that adding ":string" or "message=" might help, but it doesn't.
Does anyone know how to get the compiler to properly resolve the method overload issue? Also, if anyone can link to a definitive resource for this issue, that would be amazing!
Unfortuanately, this testing framework was written with only C# in mind, which means that the method overload you're trying to call expects "nullable" types:
void AreEqual<T>(T? expected, T? actual, string? message)
(Nullable types are a truly hideous feature of C# that they've had to introduce because null values are so common in that language, but I digress.)
So, to help F# find the correct overload, you have to explicitly make your values nullable:
open System
...
Assert.AreEqual(Nullable true, Nullable false, "message string")
The silver lining in this ugly cloud is that you can at least leave off the explicit <Nullable<bool>>
type argument, because F# will infer that for you.
As a workaround, if this is something you need to call often, you could also define your own AreEqual
member:
type Assert with
static member inline AreEqual(expected : bool, actual : bool, message : string) =
Assert.AreEqual(Nullable expected, Nullable actual, message)
And then you can call it normally:
Assert.AreEqual(true, false, "message string")