即使在CallBase=true/false之后,原始方法仍在Moq中被调用

本文关键字:方法 Moq 原始 调用 之后 CallBase true false | 更新日期: 2023-09-27 18:00:57

这是我的代码:

public class Bar { }
public class Foo { public string Name { get; set; } public Bar TheBar { get; set; } }
public class Dependency
{
    public Foo DoSomething(Expression<Func<Foo, bool>> exp1) { return new Foo(); }
}
public class Base
{
    public Dependency Dependency { get; set; }
    public virtual Foo MethodA(Expression<Func<Foo, bool>> exp1,
                               params Expression<Func<Foo, object>>[] exp2)
    {
        return Dependency.DoSomething(exp1);
    }
}
public class Derived : Base
{
    public Foo DerviedMethod(string str)
    {
        return base.MethodA(e1 => e1.Name.Equals(str), e2 => e2.TheBar);
    }
}

我的单元测试代码:

var mock = new Mock<Derived> { CallBase = true }; // Same result with false
mock
   .Setup(m => m.MethodA(
      It.IsAny<Expression<Func<Foo, bool>>>(),
      It.IsAny<Expression<Func<Foo, object>>>()
      ))
   .Returns(new Foo());
// Act
var result = mock.Object.DerviedMethod("test");
// Assert
Assert.IsNotNull(result);

但它仍然调用原始方法,而不是被嘲笑的方法。两个类都存在于同一程序集中。

我搜索过它,几乎所有人都用CallBase = truefalse做对了。

你知道上面的代码出了什么问题吗?

即使在CallBase=true/false之后,原始方法仍在Moq中被调用

正如@Pierre Luc在评论中所建议的那样,提取基类并将其作为依赖项注入可能是更好的方法(我一直认为嘲笑你实际上试图测试的类感觉是错误的(。

也就是说,为了能够模拟类的调用,它需要通过VTable进行。从本质上讲,mocking框架创建了虚拟方法的新实现。当您正常调用它时,会运行此版本的方法,然后可以拦截调用。有问题的线路是这样的:

return base.MethodA(e1 => e1.Name.Equals(str), e2 => e2.TheBar);

因为通过base关键字显式调用MethodA,所以它告诉编译器调用该方法的特定版本。总是调用基本实现。这意味着mock无法拦截该调用。

将方法更改为:

public Foo DerviedMethod(string str) {
    return MethodA(e1 => e1.Name.Equals(str), e2 => e2.TheBar);
}

允许对MethodA方法进行模拟。从设计的角度来看,这是否是正确的事情取决于你。