Faking Return Value with F # and FakeItEasy

I am trying to use FakeItEasy to model an interface defined in C #

public interface IMyInterface { int HeartbeatInterval { get; set; } } 

In the F # test, I do

 let myFake = A.Fake<IMyInterface>() A.CallTo(fun () -> ((!myFake).HeartbeatInterval)).Returns(10) |> ignore 

Running this in a test runner results in

System.ArgumentException Expression of type 'Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Int32]' cannot be used for return type 'System.Int32'

It actually seems like he is doing this for any type of return, for example. if HeartbeatInterval returned type foo , then an exception would be foo for type foo instead of System.Int32 .

Am I doing this wrong or is there some incompatibility between F # and FakeItEasy?

In my opinion, using an Expression object might be easier.

+5
source share
1 answer

I would risk the hypothesis that “Easy” in “FakeItEasy” means “ Easy Over Simple ”. The library says this is “easy,” and this is probably the case if you use it with C #. Because it is clearly designed for use with this language. But it is far from “simple” because it uses special syntax tricks with C #, which are hidden from view and do not work in F #.

The specific information you are getting right now is a combination of two things: (1) F # functions do not match C # Func<T,R> , and (2) F # overload resolution rules are different from C #.

There are three CallTo overloads - two of them take Expression<_> , and the third is "everything" by taking an object . In C #, if you call this method with a lambda expression as an argument, the compiler will try your best to convert the lambda expression to Expression<_> and call one of the specialized methods. F #, however, does not do this: F # support for the C # style Expression<_> very limited, primarily focused on LINQ compatibility, and only works when there are no alternatives. Thus, in this case, F # selects CallTo(object) overload.

Next, what will be the argument? F # is a very strict and consistent language. In addition to some special interaction cases, most F # expressions are of a certain type, regardless of the context in which they appear. In particular, an expression of the form fun() -> x will be of type unit -> 'a , where' a is of type x . In other words, this is an F # function.

At run time, the F # functions are represented by the FSharpFunc<T,R> , so the compiler switches to the CallTo(object) method, which will look at it and, not realizing that this is a hell exception.

To fix this, you can make yourself a special version of CallTo (let it be called FsCallTo ) that will force the F # compiler to convert your fun() -> x Expression<_> to Expression<_> , and then use this method instead of of CallTo :

 // WARNING: unverified code. Should work, but I haven't checked. type A with // This is how you declare extension methods in F# static member FsCallTo( e: System.Linq.Expressions.Expression<System.Func<_,_>> ) = A.CallTo( e ) let myFake = A.Fake<IMyInterface>() // Calling FsCallTo will force F# to generate an `Expression<_>`, // because that what the method expects: A.FsCallTo(fun () -> ((!myFake).HeartbeatInterval)).Returns(10) |> ignore 

However , as you absolutely correctly noticed, this is too big a problem for mocking the interface, since F # already has a completely statically verifiable, time-free, syntactically nice alternative in the form of object expressions:

 let myFake = { new IMyInterface with member this.HeartbeatInterval = 10 member this.HeartbeatInterval with set _ = () } 

I would recommend them with them.

+6
source

Source: https://habr.com/ru/post/1273581/


All Articles