【问题标题】:Moq - Is it possible to setup a mock without using It.IsAnyMoq - 是否可以在不使用 It.IsAny 的情况下设置模拟
【发布时间】:2021-10-26 13:21:23
【问题描述】:

我一直使用 Moq 进行单元测试。有时虽然我在模拟具有很多参数的方法。

想象一个这样的方法:

public class WorkClient {

public void DoSomething(string itemName, 
   int itemCount, 
   ServiceClientCredential cred, 
   CancellationToken = default(CancellationToken){}
}

当我去设置一个模拟时,我最终不得不做很多It.IsAny<T>()。我通常为每个测试制作一个模拟实例,所以我不关心匹配参数。

但我的模拟仍然是这个样子

var newMockClient = new Mock<WorkClient>();
newMockClient.Setup(x => x.DoSomething(
   It.IsAny<string>(), 
   It.IsAny<int>(),
   It.IsAny<ServiceClientCredential(),
   It.IsAny<CancellationToken>())
   .Returns(blah);

我希望能够懒惰地使用 LazySetup(如果存在),就像这样。

newMockClient.Setup(x=>x.DoSomething()).Returns(blah);

有没有这样的懒惰模式?

【问题讨论】:

  • 不,我不相信这是可能的。 Moq 设置要求您传递特定值或 It.Is()It.IsAny() 的某些变体
  • 请查看this gist。有了它,您可以编写半惰性设置,例如:SetupIgnoreArgs(x =&gt; x.DoSomething(null, null, null, null, null).Returns(blah)。我不确定它是否适用于值类型,但我认为值得一试
  • @PeterCsala 如果您发布此链接和示例用法作为答案,我会接受。它非常适合我需要的目的,所以你应该因为分享它而获得赞誉:)
  • @FoxDeploy 考虑完成,谢谢。

标签: c# .net unit-testing moq


【解决方案1】:

基于此gist,您可以为SetupDefaultArgs 创建一个重载,它适用于void 返回类型。您需要添加对Moq.Language.FlowSystem.Linq.Expressions; 的引用

public static ISetup<T> SetupDefaultArgs<T>(this Mock<T> mock, string methodName)
    where T : class
{
    var method = typeof(T).GetMethod(methodName);
    if (method == null)
        throw new ArgumentException($"No method named '{methodName}' exists on type '{typeof(T).Name}'");
            
    var instance = Expression.Parameter(typeof(T), "m");
    var callExp = Expression.Call(instance, method, method.GetParameters().Select(p => GenerateItIsAny(p.ParameterType)));
    var exp = Expression.Lambda<Action<T>>(callExp, instance);
    return mock.Setup(exp);
}

//helper method for above
private static MethodCallExpression GenerateItIsAny(Type T)
    {
        var ItIsAnyT = typeof(It)
            .GetMethod("IsAny")
            .MakeGenericMethod(T);
        return Expression.Call(ItIsAnyT);
    }

因此,在您的情况下,用法如下所示:

public interface IWorkClient
{
    void DoSomething(string itemName, int itemCount,
        ServiceClientCredential cred,
        CancellationToken token = default(CancellationToken));
}
var mock = new Mock<IWorkClient>();
mock.SetupDefaultArgs(nameof(IWorkClient.DoSomething));

要确保它已被调用,您可以执行以下操作:

//Arrange
var mock = new Mock<IWorkClient>();
mock.SetupDefaultArgs(nameof(IWorkClient.DoSomething))
    .Callback(() => Console.WriteLine("DoSomething has been called"));

var cts = new CancellationTokenSource();

//Act       
mock.Object.DoSomething("1", 2, null, cts.Token);

//Assert
mock.Verify(client => client.DoSomething("1", 2, null, cts.Token), Times.Once);

【讨论】:

  • @FoxDeploy 感谢您的编辑,我忘记复制了。
猜你喜欢
  • 2021-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多