Foq icon indicating copy to clipboard operation
Foq copied to clipboard

Mock throws InvalidProgramException when Calls() or Raises() infer wrong object kind

Open mrfootoyou opened this issue 1 year ago • 0 comments

Member functions mocked using Foq.ResultBuilder.Calls will throw System.InvalidProgramException when a value type parameter is inferred to be a reference type argument. See the example below.

I'm not sure there is anything Foq can do to mitigate this short of developing an Analyzer. Calls() and Raises() are already marked as RequiresExplicitTypeArguments, but that only ensures the type list ("<...>") exists; it doesn't apply to the individual type args.

However, Foq can easily document this behavior. This issue will also serve that purpose, but it would be more helpful if the information was in the documentation of the Calls() and Raises() functions.

Example

module FoqCallsTests
open System
open Xunit
open Foq

type SomeValueType = int
type SomeRefType = string
type IFoo =
    abstract member Foo: byValueArg: SomeValueType * byRefArg: SomeRefType -> unit

[<Fact>]
let ``Foq.Calls with inferred ValueType arg throws InvalidProgramException``() =
    let m =
        Mock<IFoo>()
            .Setup(fun m -> <@ m.Foo(any (), any ()) @>)
            .Calls<_ * _>(fun (v, r) -> ())  // <== both args are inferred to be `obj`
            .Create()

    Assert.Throws<InvalidProgramException>(fun _ -> m.Foo(123, "abc"))

[<Fact>]
let ``Foq.Calls with explicit ValueType arg is okay``() =
    let m =
        Mock<IFoo>()
            .Setup(fun m -> <@ m.Foo(any (), any ()) @>)
            .Calls<SomeValueType * _>(fun (v, r) -> ()) // <== arg2 is inferred to be `obj` which is okay
            .Create()

    m.Foo(123, "abc") // works as expected

mrfootoyou avatar Jan 31 '25 19:01 mrfootoyou