This post explains how to configure AutoFixture.AutoFoq 3 so that null
values are never returned when using Foq 1.5.1 and newer.
Foq’s behavior for mock objects that have not been explicitly setup, is to return null
if the return type of a function is a reference type (e.g. a string):
type IInterface =
abstract DoSomething : unit -> string
let sut = Mock<IInterface>().Create()
// No expectations have been setup.
let actual = sut.DoSomething()
// -> actual = null
AutoFixture is an opinionated library, and one of the opinions it holds is that nulls are invalid return values.
By the time AutoFixture.AutoFoq 3 was published Foq was still in version 1.0 with no specific hooks to override this behavior.
Foq now provides the necessary hooks to override this behavior and the rest of this post explains how to automate this when using AutoFixture.AutoFoq.
Ruben Bartelink originally discussed and proposed in Foq discussions about a returnStrategy
argument for members that have not been explicitly setup:
let sut = Mock<IInterface>(returnStrategy = fun x -> "123" :> obj).Create()
// No expectations have been setup - fallback to returnStrategy function.
let actual = sut.DoSomething()
// -> actual = "123"
The existing AutoFoqCustomization has no specific hook to select Foq’s new returnStrategy
argument. This can be addressed when necessary with the customization shown below:
[<AutoOpen>]
module internal SynthesizerMethod =
type private SynthesizerMethod<'T when 'T : not struct>
(parameterInfos, builder) =
interface IMethod with
member this.Parameters = parameterInfos
member this.Invoke parameters =
Mock<'T>(SpecimenContext(builder).Resolve).Create(
parameters |> Seq.toArray) :> obj
let Create (targetType: Type, parameterInfos: ParameterInfo[], builder) =
Activator.CreateInstance(
typedefof<SynthesizerMethod<_>>
.MakeGenericType(targetType),
parameterInfos,
builder)
[<AutoOpen>]
module internal SynthesizerType =
type Type with
member this.GetPublicAndProtectedConstructors () =
this.GetConstructors(
BindingFlags.Public |||
BindingFlags.Instance |||
BindingFlags.NonPublic)
type internal SynthesizerMethodQuery (builder) =
do if builder = null then raise <| ArgumentNullException("builder")
interface IMethodQuery with
member this.SelectMethods target =
if target = null then raise <| ArgumentNullException("target")
if target.IsInterface then
seq { yield Create(target, Array.empty, builder) :?> IMethod }
else
target.GetPublicAndProtectedConstructors()
|> Seq.sortBy(fun x -> x.GetParameters().Length)
|> Seq.map(fun ctor ->
Create(target, ctor.GetParameters(), builder) :?> IMethod)
member this.SelectMethods targetType =
(this :> IMethodQuery).SelectMethods targetType
type internal AutoFoqSynthesizeReturnValuesCustomization () =
interface ICustomization with
member this.Customize fixture =
match fixture with
| null -> raise (ArgumentNullException("fixture"))
| _ -> fixture.Customizations.Add(
FilteringSpecimenBuilder(
MethodInvoker(
SynthesizerMethodQuery(fixture)),
AbstractTypeSpecification()))
member this.Customize fixture = (this :> ICustomization).Customize fixture
The only difference from the original AutoFoqCustomization is the usage of Foq’s returnStrategy
argument in the SynthesizerMethodQuery
class.
[<Fact>]
let CustomizationFillsReturnValues () =
let fixture = Fixture().Customize(AutoFoqSynthesizeReturnValuesCustomization())
let sut = fixture.Create<IInterface>()
// No expectations have been setup - fallback to Foq's returnStrategy function.
let actual = sut.DoSomething()
// -> actual: "f5cdf6b1-a473-410f-95f3-f427f7abb0c7"
The above test can be also written declaratively using AutoData theories:
type TestConventionsAttribute() =
inherit AutoDataAttribute(
Fixture().Customize(AutoFoqSynthesizeReturnValuesCustomization()))
[<Theory; TestConventions>]
let CustomizationFillsReturnValuesDecleratively (sut : IInterface) =
let actual = sut.DoSomething()
// -> actual: "f5cdf6b1-a473-410f-95f3-f427f7abb0c7"