Search code examples
c#vb.nettddmoq

Setup.Callback() is not being called


I have a Setup.Callback() action that's not being called in VB.NET. It works as expected in C#. The code conversion is correct.

VB.NET

Private Function CreateFriendEditViewModel() As IFriendEditViewModel
  Dim oWrapper As FriendWrapper
  Dim oFriend As [Friend]
  Dim oMock As Mock(Of IFriendEditViewModel)

  oMock = New Mock(Of IFriendEditViewModel)
  oMock.
    Setup(Sub(Vm) Vm.Load(It.IsAny(Of Integer))).
    Callback(Of Integer?)(Sub(FriendId)
                            oFriend = New [Friend] With {.Id = FriendId.Value}
                            oWrapper = New FriendWrapper(oFriend)

                            oMock.
                              Setup(Function(Vm) Vm.Friend).
                              Returns(oWrapper)
                          End Sub)

  Me.FriendEditViewModelMocks.Add(oMock)

  Return oMock.Object
End Function

C#

private IFriendEditViewModel CreateFriendEditViewModel()
{
  var friendEditViewModelMock = new Mock<IFriendEditViewModel>();
  friendEditViewModelMock
    .Setup(vm => vm.Load(It.IsAny<int>()))
    .Callback<int?>(friendId =>
    {
      friendEditViewModelMock
        .Setup(vm => vm.Friend)
        .Returns(new FriendWrapper(new Friend { Id = friendId.Value }));
    });
  _friendEditViewModelMocks.Add(friendEditViewModelMock);
  return friendEditViewModelMock.Object;
}

Note that it works in VB.NET when I change the Nullable(Of Integer) type to Integer.

While grabbing at straws, I tried this slight variation:

oMock.
  Setup(Function(Vm) Vm.Friend).
  Returns(New FriendWrapper(New [Friend] With {.Id = FriendId.Value}))

...but it didn't help.

Neither did any of these three Q&As shed light on the situation:

I'm very much a beginner at Moq, so this has got me befuddled.

Can anyone spot what's going wrong here? Why isn't my callback being called, and why is this failure occurring only in VB.NET?

--UPDATE--

I had a look at the IL (via JustDecompile) for each of these four scenarios:

  1. VB.NET using type Integer (succeeds)
Callback(Of Integer)(Sub(FriendId)
                       oMock.
                          Setup(Function(Vm) Vm.Friend).
                          Returns(New FriendWrapper(New [Friend] With {.Id = FriendId}))
                     End Sub)

.method private instance class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel CreateFriendEditViewModel () cil managed
{
    .locals init (
        [0] class FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0' $VB$Closure_0,
        [1] class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel CreateFriendEditViewModel,
        [2] class [System.Core]System.Linq.Expressions.ParameterExpression V_2
    )

    IL_0000: nop
    IL_0001: newobj instance void FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::.ctor()
    IL_0006: stloc.0
    IL_0007: ldloc.0
    IL_0008: newobj instance void class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>::.ctor()
    IL_000d: stfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_0012: ldloc.0
    IL_0013: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_0018: ldtoken [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel
    IL_001d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0022: ldstr "Vm"
    IL_0027: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type,  string)
    IL_002c: stloc.2
    IL_002d: ldloc.2
    IL_002e: ldtoken method instance void [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel::Load(valuetype [mscorlib]System.Nullable`1<int32>)
    IL_0033: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0038: castclass [mscorlib]System.Reflection.MethodInfo
    IL_003d: ldc.i4.1
    IL_003e: newarr [System.Core]System.Linq.Expressions.Expression
    IL_0043: dup
    IL_0044: ldc.i4.0
    IL_0045: ldnull
    IL_0046: ldtoken method int32 [Moq]Moq.It::IsAny<int32>()
    IL_004b: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0050: castclass [mscorlib]System.Reflection.MethodInfo
    IL_0055: ldc.i4.0
    IL_0056: newarr [System.Core]System.Linq.Expressions.Expression
    IL_005b: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_0060: ldtoken valuetype [mscorlib]System.Nullable`1<int32>
    IL_0065: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_006a: call class [System.Core]System.Linq.Expressions.UnaryExpression [System.Core]System.Linq.Expressions.Expression::ConvertChecked(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Type)
    IL_006f: stelem.ref
    IL_0070: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_0075: ldc.i4.1
    IL_0076: newarr [System.Core]System.Linq.Expressions.ParameterExpression
    IL_007b: dup
    IL_007c: ldc.i4.0
    IL_007d: ldloc.2
    IL_007e: stelem.ref
    IL_007f: call class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>>(class [System.Core]System.Linq.Expressions.Expression,  class [System.Core]System.Linq.Expressions.ParameterExpression[])
    IL_0084: callvirt instance class [Moq]Moq.Language.Flow.ISetup`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>::Setup(class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<!0>>)
    IL_0089: ldloc.0
    IL_008a: ldftn instance void FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::_Lambda$__0(int32)
    IL_0090: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object,  native int)
    IL_0095: callvirt instance class [Moq]Moq.Language.Flow.ICallbackResult [Moq]Moq.Language.ICallback::Callback<int32>(class [mscorlib]System.Action`1<int32>)
    IL_009a: pop
    IL_009b: ldarg.0
    IL_009c: ldfld class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>> FriendStorage.UI.Tests.ViewModels.MainViewModelTests::FriendEditViewModelMocks
    IL_00a1: ldloc.0
    IL_00a2: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_00a7: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>>::Add(!0)
    IL_00ac: nop
    IL_00ad: ldloc.0
    IL_00ae: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_00b3: callvirt instance class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>::get_Object()
    IL_00b8: stloc.1
    IL_00b9: br.s IL_00bb

    IL_00bb: ldloc.1
    IL_00bc: ret
}
  1. VB.NET using type Integer? (fails)
Callback(Of Integer?)(Sub(FriendId)
                        oMock.
                          Setup(Function(Vm) Vm.Friend).
                          Returns(New FriendWrapper(New [Friend] With {.Id = FriendId.Value}))
                      End Sub)

.method private instance class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel CreateFriendEditViewModel () cil managed
{
    .locals init (
        [0] class FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0' $VB$Closure_0,
        [1] class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel CreateFriendEditViewModel,
        [2] class [System.Core]System.Linq.Expressions.ParameterExpression V_2
    )

    IL_0000: nop
    IL_0001: newobj instance void FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::.ctor()
    IL_0006: stloc.0
    IL_0007: ldloc.0
    IL_0008: newobj instance void class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>::.ctor()
    IL_000d: stfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_0012: ldloc.0
    IL_0013: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_0018: ldtoken [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel
    IL_001d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0022: ldstr "Vm"
    IL_0027: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type,  string)
    IL_002c: stloc.2
    IL_002d: ldloc.2
    IL_002e: ldtoken method instance void [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel::Load(valuetype [mscorlib]System.Nullable`1<int32>)
    IL_0033: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0038: castclass [mscorlib]System.Reflection.MethodInfo
    IL_003d: ldc.i4.1
    IL_003e: newarr [System.Core]System.Linq.Expressions.Expression
    IL_0043: dup
    IL_0044: ldc.i4.0
    IL_0045: ldnull
    IL_0046: ldtoken method int32 [Moq]Moq.It::IsAny<int32>()
    IL_004b: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0050: castclass [mscorlib]System.Reflection.MethodInfo
    IL_0055: ldc.i4.0
    IL_0056: newarr [System.Core]System.Linq.Expressions.Expression
    IL_005b: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_0060: ldtoken valuetype [mscorlib]System.Nullable`1<int32>
    IL_0065: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_006a: call class [System.Core]System.Linq.Expressions.UnaryExpression [System.Core]System.Linq.Expressions.Expression::ConvertChecked(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Type)
    IL_006f: stelem.ref
    IL_0070: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_0075: ldc.i4.1
    IL_0076: newarr [System.Core]System.Linq.Expressions.ParameterExpression
    IL_007b: dup
    IL_007c: ldc.i4.0
    IL_007d: ldloc.2
    IL_007e: stelem.ref
    IL_007f: call class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>>(class [System.Core]System.Linq.Expressions.Expression,  class [System.Core]System.Linq.Expressions.ParameterExpression[])
    IL_0084: callvirt instance class [Moq]Moq.Language.Flow.ISetup`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>::Setup(class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<!0>>)
    IL_0089: ldloc.0
    IL_008a: ldftn instance void FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::_Lambda$__0(valuetype [mscorlib]System.Nullable`1<int32>)
    IL_0090: newobj instance void class [mscorlib]System.Action`1<valuetype [mscorlib]System.Nullable`1<int32>>::.ctor(object,  native int)
    IL_0095: callvirt instance class [Moq]Moq.Language.Flow.ICallbackResult [Moq]Moq.Language.ICallback::Callback<valuetype [mscorlib]System.Nullable`1<int32>>(class [mscorlib]System.Action`1<valuetype [mscorlib]System.Nullable`1<int32>>)
    IL_009a: pop
    IL_009b: ldarg.0
    IL_009c: ldfld class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>> FriendStorage.UI.Tests.ViewModels.MainViewModelTests::FriendEditViewModelMocks
    IL_00a1: ldloc.0
    IL_00a2: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_00a7: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>>::Add(!0)
    IL_00ac: nop
    IL_00ad: ldloc.0
    IL_00ae: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel> FriendStorage.UI.Tests.ViewModels.MainViewModelTests/'_Closure$__5-0'::$VB$Local_oMock
    IL_00b3: callvirt instance class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModels.IFriendEditViewModel>::get_Object()
    IL_00b8: stloc.1
    IL_00b9: br.s IL_00bb

    IL_00bb: ldloc.1
    IL_00bc: ret
}
  1. C# using type int (succeeds)
.Callback<int>(friendId =>
{
  friendEditViewModelMock
    .Setup(vm => vm.Friend)
    .Returns(new FriendWrapper(new Friend { Id = friendId }));
});

.method private hidebysig instance class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel CreateFriendEditViewModel () cil managed
{
    .locals init (
        [0] class FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0' 'CS$<>8__locals0',
        [1] class [System.Core]System.Linq.Expressions.ParameterExpression V_1,
        [2] class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel V_2
    )

    IL_0000: newobj instance void FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::.ctor()
    IL_0005: stloc.0
    IL_0006: nop
    IL_0007: ldloc.0
    IL_0008: newobj instance void class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>::.ctor()
    IL_000d: stfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_0012: ldloc.0
    IL_0013: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_0018: ldtoken [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel
    IL_001d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0022: ldstr "vm"
    IL_0027: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type,  string)
    IL_002c: stloc.1
    IL_002d: ldloc.1
    IL_002e: ldtoken method instance void [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel::Load(int32)
    IL_0033: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0038: castclass [mscorlib]System.Reflection.MethodInfo
    IL_003d: ldc.i4.1
    IL_003e: newarr [System.Core]System.Linq.Expressions.Expression
    IL_0043: dup
    IL_0044: ldc.i4.0
    IL_0045: ldnull
    IL_0046: ldtoken method int32 [Moq]Moq.It::IsAny<int32>()
    IL_004b: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0050: castclass [mscorlib]System.Reflection.MethodInfo
    IL_0055: call class [System.Core]System.Linq.Expressions.Expression[] [mscorlib]System.Array::Empty<class [System.Core]System.Linq.Expressions.Expression>()
    IL_005a: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_005f: stelem.ref
    IL_0060: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_0065: ldc.i4.1
    IL_0066: newarr [System.Core]System.Linq.Expressions.ParameterExpression
    IL_006b: dup
    IL_006c: ldc.i4.0
    IL_006d: ldloc.1
    IL_006e: stelem.ref
    IL_006f: call class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>>(class [System.Core]System.Linq.Expressions.Expression,  class [System.Core]System.Linq.Expressions.ParameterExpression[])
    IL_0074: callvirt instance class [Moq]Moq.Language.Flow.ISetup`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>::Setup(class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<!0>>)
    IL_0079: ldloc.0
    IL_007a: ldftn instance void FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::'<CreateFriendEditViewModel>b__1'(int32)
    IL_0080: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object,  native int)
    IL_0085: callvirt instance class [Moq]Moq.Language.Flow.ICallbackResult [Moq]Moq.Language.ICallback::Callback<int32>(class [mscorlib]System.Action`1<int32>)
    IL_008a: pop
    IL_008b: ldarg.0
    IL_008c: ldfld class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>> FriendStorage.UITests.ViewModel.MainViewModelTests::_friendEditViewModelMocks
    IL_0091: ldloc.0
    IL_0092: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_0097: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>>::Add(!0)
    IL_009c: nop
    IL_009d: ldloc.0
    IL_009e: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_00a3: callvirt instance class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>::get_Object()
    IL_00a8: stloc.2
    IL_00a9: br.s IL_00ab

    IL_00ab: ldloc.2
    IL_00ac: ret
}
  1. C# using type int? (succeeds)
.Callback<int?>(friendId =>
{
  friendEditViewModelMock
    .Setup(vm => vm.Friend)
    .Returns(new FriendWrapper(new Friend { Id = friendId.Value }));
});

.method private hidebysig instance class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel CreateFriendEditViewModel () cil managed
{
    .locals init (
        [0] class FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0' 'CS$<>8__locals0',
        [1] class [System.Core]System.Linq.Expressions.ParameterExpression V_1,
        [2] class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel V_2
    )

    IL_0000: newobj instance void FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::.ctor()
    IL_0005: stloc.0
    IL_0006: nop
    IL_0007: ldloc.0
    IL_0008: newobj instance void class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>::.ctor()
    IL_000d: stfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_0012: ldloc.0
    IL_0013: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_0018: ldtoken [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel
    IL_001d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0022: ldstr "vm"
    IL_0027: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type,  string)
    IL_002c: stloc.1
    IL_002d: ldloc.1
    IL_002e: ldtoken method instance void [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel::Load(valuetype [mscorlib]System.Nullable`1<int32>)
    IL_0033: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0038: castclass [mscorlib]System.Reflection.MethodInfo
    IL_003d: ldc.i4.1
    IL_003e: newarr [System.Core]System.Linq.Expressions.Expression
    IL_0043: dup
    IL_0044: ldc.i4.0
    IL_0045: ldnull
    IL_0046: ldtoken method int32 [Moq]Moq.It::IsAny<int32>()
    IL_004b: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
    IL_0050: castclass [mscorlib]System.Reflection.MethodInfo
    IL_0055: call class [System.Core]System.Linq.Expressions.Expression[] [mscorlib]System.Array::Empty<class [System.Core]System.Linq.Expressions.Expression>()
    IL_005a: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_005f: ldtoken valuetype [mscorlib]System.Nullable`1<int32>
    IL_0064: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0069: call class [System.Core]System.Linq.Expressions.UnaryExpression [System.Core]System.Linq.Expressions.Expression::Convert(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Type)
    IL_006e: stelem.ref
    IL_006f: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression,  class [mscorlib]System.Reflection.MethodInfo,  class [System.Core]System.Linq.Expressions.Expression[])
    IL_0074: ldc.i4.1
    IL_0075: newarr [System.Core]System.Linq.Expressions.ParameterExpression
    IL_007a: dup
    IL_007b: ldc.i4.0
    IL_007c: ldloc.1
    IL_007d: stelem.ref
    IL_007e: call class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Action`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>>(class [System.Core]System.Linq.Expressions.Expression,  class [System.Core]System.Linq.Expressions.ParameterExpression[])
    IL_0083: callvirt instance class [Moq]Moq.Language.Flow.ISetup`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>::Setup(class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Action`1<!0>>)
    IL_0088: ldloc.0
    IL_0089: ldftn instance void FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::'<CreateFriendEditViewModel>b__1'(valuetype [mscorlib]System.Nullable`1<int32>)
    IL_008f: newobj instance void class [mscorlib]System.Action`1<valuetype [mscorlib]System.Nullable`1<int32>>::.ctor(object,  native int)
    IL_0094: callvirt instance class [Moq]Moq.Language.Flow.ICallbackResult [Moq]Moq.Language.ICallback::Callback<valuetype [mscorlib]System.Nullable`1<int32>>(class [mscorlib]System.Action`1<valuetype [mscorlib]System.Nullable`1<int32>>)
    IL_0099: pop
    IL_009a: ldarg.0
    IL_009b: ldfld class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>> FriendStorage.UITests.ViewModel.MainViewModelTests::_friendEditViewModelMocks
    IL_00a0: ldloc.0
    IL_00a1: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_00a6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>>::Add(!0)
    IL_00ab: nop
    IL_00ac: ldloc.0
    IL_00ad: ldfld class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel> FriendStorage.UITests.ViewModel.MainViewModelTests/'<>c__DisplayClass6_0'::friendEditViewModelMock
    IL_00b2: callvirt instance class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel class [Moq]Moq.Mock`1<class [FriendStorage.UI]FriendStorage.UI.ViewModel.IFriendEditViewModel>::get_Object()
    IL_00b7: stloc.2
    IL_00b8: br.s IL_00ba

    IL_00ba: ldloc.2
    IL_00bb: ret
}

I had hoped something would jump out and be easy to spot, but unfortunately IL is still very much a mystery to me. That said, I do notice a significant difference between the VB and C# versions. In fact, there's even quite a difference between the C# int/int? versions. Not so much between Integer/Integer? in VB.

As a safety check, I implemented a Try/Catch as well:

Callback(Of Integer?)(Sub(FriendId)
                        Try
                          oMock.
                            Setup(Function(Vm) Vm.Friend).
                            Returns(New FriendWrapper(New [Friend] With {.Id = FriendId.Value}))

                        Catch ex As Exception
                          Debug.WriteLine(ex.Message)

                        End Try
                      End Sub)

Still no change. The callback simply isn't called when using a Nullable(Of Integer) in VB. I'd love to set a breakpoint somewhere in there, but it won't do any good if execution never reaches that code block.

This is a show-stopper for my current project, so I'm calling in all experts.


Solution

  • OK, got it.

    Here's the IFriendEditViewModel signature:

    Public Interface IFriendEditViewModel
      Sub Load(FriendId As Integer?)
      ReadOnly Property [Friend] As FriendWrapper
    End Interface
    

    ...and here's the original setup/callback again:

    Private Function CreateFriendEditViewModel() As IFriendEditViewModel
      Dim oMock = New Mock(Of IFriendEditViewModel)
    
      oMock.
        Setup(Sub(Vm) Vm.Load(It.IsAny(Of Integer))).
        Callback(Of Integer?)(Sub(FriendId)
                                oMock.
                                  Setup(Function(Vm) Vm.Friend).
                                  Returns(New FriendWrapper(New [Friend] With {.Id = FriendId.Value}))
                              End Sub)
    
      Me.FriendEditViewModelMocks.Add(oMock)
    
      Return oMock.Object
    End Function
    

    See the problem? The Vm.Load() method takes a Nullable(Of Integer). I'd missed this little detail earlier; I'm converting from C#, which apparently allows the mixing of parameter types in this case. Contrary to one's expectations, this builds and runs just fine:

    private IFriendEditViewModel CreateFriendEditViewModel()
    {
      var friendEditViewModelMock = new Mock<IFriendEditViewModel>();
      friendEditViewModelMock
        .Setup(vm => vm.Load(It.IsAny<int>())) // <== Oops!
        .Callback<int?>(friendId =>
        {
          friendEditViewModelMock
            .Setup(vm => vm.Friend)
            .Returns(new FriendWrapper(new Friend { Id = friendId.Value }));
        });
      _friendEditViewModelMocks.Add(friendEditViewModelMock);
      return friendEditViewModelMock.Object;
    }
    

    So, I just changed the parameter for my setup to this:

    Setup(Sub(Vm) Vm.Load(It.IsAny(Of Integer?)))
    

    ...and it sailed right through. The callback is called as expected.

    In fact, I'm not entirely sure I wouldn't call this a bug in the C# compiler.