Moq It.Is <>不匹配

Eri*_*kTJ 7 c# unit-testing moq mocking

这段代码:

hub.MockedUserRepository.Setup(r => r.Update(It.IsAny<ControllUser>()))
                        .Callback((ControllUser usr) => Console.WriteLine("NULL = " + (usr.Zombies[0].ConnectionId == null)))
                        .Verifiable();
Run Code Online (Sandbox Code Playgroud)

会打印

NULL = True

所以我在想使用这种匹配会抓住它:

var zombieDisconnectParameterMatcher = It.Is<ControllUser>(x => x.Zombies[0].ConnectionId == null);
hub.MockedUserRepository.Setup(r => r.Update(zombieDisconnectParameterMatcher))
                        .Callback((ControllUser usr) => Console.WriteLine("NULL = " + (usr.Zombies[0].ConnectionId == null)))
                        .Verifiable();
Run Code Online (Sandbox Code Playgroud)

但事实并非如此.

为什么?

Car*_*iel 9

通过查看源代码It,它与表达式树有关.我喜欢这个问题; 他们可能很令人费解.如果您要查看以下方法定义:

public static TValue It.Is<TValue>(Expression<Func<TValue, bool>> match)
{
        return Match<TValue>.Create(
                value => match.Compile().Invoke(value),
                () => It.Is<TValue>(match));
}

public static T Match.Create<T>(Predicate<T> condition, Expression<Func<T>> renderExpression)
{
        // ...
        return default(T);
}
Run Code Online (Sandbox Code Playgroud)

如果您要执行以下行:

var zombieDisconnectParameterMatcher = It.Is<ControllUser>(x => x.Zombies[0].ConnectionId == null);
Run Code Online (Sandbox Code Playgroud)

然后It.Is<ControllUser>()将尝试调用一个名为的方法Match.Create<ControllUser>(),该方法返回默认值ControllUser.我认为ControllUser是一个类,因此zombieDisconnectParameterMatcher将是null.您应该能够通过调试器看到这一点.所以你实际上是在呼唤:

hub.MockedUserRepository.Setup(r => r.Update(null))
    .Callback((ControllUser usr) => Console.WriteLine("NULL = " + (usr.Zombies[0].ConnectionId == null)))
    .Verifiable();
Run Code Online (Sandbox Code Playgroud)

Update使用非null 执行方法时ControllUser(例如,来自正在测试的方法),回调将不会触发.它根本不符合标准,因为它不是空的.您也会看到验证失败.

要解决此问题,可以内联zombieDisconnectParameterMatcher变量,或使其成为表达式类型变量(例如Expression<Func<...>>).后者将确保代码不被执行,但被视为模拟框架可以推理的表达式(' Update被调用Zombies[0].ConnectionId == null?').