1

I am using F# and Foq to write unit tests for a C# project.

I am trying to set up a mock of an interface whose method has an out parameter, and I have no idea how to even start. It probably has to do with code quotations, but that's where my understanding ends.

The interface is this:

public interface IGetTypeNameString
{
    bool For(Type type, out string typeName);
}

In C# Foq usage for the interface looks like this:

[Fact]
public void Foq_Out()
{
    // Arrange
    var name = "result";
    var instance = new Mock<IGetTypeNameString>()
        .Setup(x => x.For(It.IsAny<Type>(), out name))
        .Returns(true)
        .Create();

    // Act
    string resultName;
    var result = instance.For(typeof(string), out resultName);

    // Assert
    Assert.True(result);
    Assert.Equal("result", resultName);
}

As for how to achieve that with F#, I am completely lost. I tried something along the lines of

let name = "result"
let instance = Mock<IGetTypeNameString>().Setup(<@ x.For(It.IsAny<Type>(), name) @>).Returns(true).Create();

which results in the quotation expression being underlined with an error message of

This expression was expected to have type IGetTypeNameString -> Quotations.Expr<'a> but here has type Quotations.Expr<'b>

Without any indication what types a and b are supposed to be, I have no clue how to correct this.

:?>

(It gets even wilder when I use open Foq.Linq; then the Error List window starts telling me about possible overloads with stuff like Action<'TAbstract> -> ActionBuilder<'TAbstract>, and I get even loster....)

Any assistance or explanation greatly appreciated!

Edit:

So, as stated here, byref/out parameters can not be used in code quotations. Can this be set up at all then in F#?

Community
  • 1
  • 1
TeaDrivenDev
  • 6,591
  • 33
  • 50

1 Answers1

3

Foq supports setting up of C# out parameters from C# using the Foq.Linq namespace.

The IGetTypeNameString interface can be easily setup in F# via an object expression:

let mock = 
    { new IGetTypeNameString with
       member __.For(t,name) = 
          name <- "Name"
          true
    }

For declarations that have no analog in F#, like C#'s protected members and out parameters, you can also use the SetupByName overload, i.e.:

let mock = 
    Mock<IGetTypeNameString>()
        .SetupByName("For").Returns(true)
        .Create()                
let success, _ = mock.For(typeof<int>)
Phillip Trelford
  • 6,513
  • 25
  • 40
  • Thanks a lot! I think the object expression will do fine for this purpose. I'd seen those, but I still need to anchor the knowledge their existence in my conscious. ;-) How would I set up the `out` parameter using `SetupByName`? I think that was one of the first things I tried, but I couldn't find out where the `out` parameter goes. – TeaDrivenDev Feb 03 '14 at 10:40
  • Currently you can only set the out parameter using `SetupByName` using Foq.Linq in C#. I'm planning to extend the functionality so that for F# you can specify the out parameter values using `Returns`, e.g. `SetupByName("For").Returns(true,"Name")`. – Phillip Trelford Feb 03 '14 at 10:55
  • I see; I think I misunderstood the `SetupByName` comment in your answer earlier. The overload to `Returns` is actually what I would have expected, yes. – TeaDrivenDev Feb 03 '14 at 11:12
  • 1
    @TeaDrivenDev I've now added support for setting up C# out parameters from F# via `SetupByName` and specifying tuples in the `Returns` just as you would use them normally in F# – Phillip Trelford Feb 05 '14 at 17:59