Moq:如何验证 Action<T> 参数是一个空函数?

Moq: How to verify that an Action<T> argument is an empty function?

在 Automapper v9 中,Map() 方法具有以下签名:

TDestination Map<TDestination>(object source, Action<IMappingOperationOptions> opts);

我们经常做如下用法:

_mapper.Map<SomeClass>(srcObj, _ => { });

问题:对于最小起订量,我如何测试第二个参数是否确实为空Action

我知道如何忽略第二个参数,但这并不是一个很好的测试...

_mapper.Setup(m => m.Map<SomeClass>(srcObj, It.IsAny<Action<IMappingOperationOptions>>()))
       .Returns(mapped);

奖励积分:我将如何测试非空 Action

我相信您可以分析操作方法的编译 (IL) 代码以确定它是否为空:

private bool IsEmptyAction(Action<IMappingOperationOptions> a)
{
    return a.Method.GetMethodBody().GetILAsByteArray().SequenceEqual(new byte[] { 0x0, 0x2a });
}

0x0, 0x2a 值是通过观察 IL 对于空操作的样子获得的。对于具有泛型类型参数的操作和具有 none.

的操作都是相同的

那么你可以像这样使用这个方法:

It.Is<Action<IMappingOperationOptions>>(a => IsEmptyAction(a))

并且显然测试相反的只是否定条件的情况(!IsEmptyAction(a))。

要测试某些值的选项,您可能需要模拟 IMappingOperationOptions:

private bool IsItemsExist(Action<IMappingOperationOptions> a, Func<IDictionary<string, object>, bool> itemsChecker)
{
    // Create mock of options
    var optionsMock = new Mock<IMappingOperationOptions>();
    var itemsDictionary = new Dictionary<string, object>();
    optionsMock.SetupGet(o => o.Items).Returns(itemsDictionary);

    // Call action
    a(optionsMock.Object);

    // Call check function
    return itemsChecker(itemsDictionary);
}

然后你可以将它应用到你的主要模拟中:

test
    .Setup(t => t.TestMethod(It.Is<Action<IMappingOperationOptions>>(a => !IsItemsExist(a, d => d.ContainsKey("test")))))
    .Throws(new Exception("must contain test"));

然后你可以这样测试它:

test.Object.TestMethod(o => o.Items.Add("test", "moo"));
Assert.Throws<Exception>(() => test.Object.TestMethod(o => o.Items.Add("hello", "test")));

当然这只是一个示例,因此您可以根据需要使其更通用或更不通用等。