使用Moq模拟基类方法调用

时间:2009-08-18 10:55:22

标签: moq

我正在修改一个类方法,该方法格式化一些输入参数日期,这些日期随后在方法调用中作为参数用于基类(它存在于另一个程序集中)。

我想验证传递给我的方法的日期在传递给基类方法时格式正确所以我想要Moq基类方法调用。 Moq可以实现吗?

6 个答案:

答案 0 :(得分:14)

截至2013年,您可以使用最新的Moq。这是example

public class ViewModelBase
{
    public virtual bool IsValid(DateTime date)
    {
        //some complex shared stuff here
    }
} 

public class MyViewModel : ViewModelBase
{
    public void Save(DateTime date)
    {
        if (IsValid(date))
        {
            //do something here
        }
    }
}

public void MyTest()
{
    //arrange
    var mockMyViewModel = new Mock<MyViewModel>(){CallBase = true};
    mockMyViewModel.Setup(x => x.IsValid(It.IsAny<DateTime>())).Returns(true);

    //act
    mockMyViewModel.Object.Save();

    //assert
    //do your assertions here
} 

答案 1 :(得分:12)

如果我正确理解你的问题,你在其他一些程序集中定义了一个A类,然后B类或多或少地实现了这样的:

public class B : A
{
    public override MyMethod(object input)
    {
        // Do something
        base.MyMethod(input);
    }
}

现在你要验证是否调用了base.MyMethod?

我不知道如何使用动态模拟库来完成此操作。所有动态模拟库(具有TypeMock的例外)通过动态发出从所讨论的类型派生的类来工作。

在你的情况下,你不能很好地要求Moq从 A 派生,因为你想测试 B

这意味着你必须要求Moq给你一个Mock<B>。但是,这意味着发出的类型派生自B,虽然它可以覆盖MyMethod(仍然是虚拟的)并调用它的基础(B.MyMethod),但它无法访问原始类并验证B调用{ {1}}。

想象一下,你必须编写一个派生自B的类(C)。虽然你可以覆盖MyMethod,但你无法验证B调用A:

base.MyMethod

同样,除了TypeMock可能的例外,动态模拟库也不能做你手动无法做的事情。

但是,我认为调用你试图验证的基本方法有一些可观察到的副作用,所以如果可能的话,你可以使用基于状态的测试而不是基于行为的测试来验证调用方法的结果吗?

在任何情况下,在大多数情况下,基于状态的测试应该是您的默认方法。

答案 2 :(得分:3)

很有可能嘲弄基类。但是你必须修改目标类。

对于前。 DerivedClass 扩展 BaseClass BaseClass 包含方法 MethodA() MethodB() MethodC() ... DerivedClass有这个方法:

void MyMethod() {
  this.MethodA();
  this.MethodB();
  this.MethodC();
}

您想要模拟基类,以验证是否正在调用所有 MethodA() MethodB() MethodC() MyMethod()

您必须在 DerivedClass

中创建一个字段
class DerivedClass {
  private BaseClass self = this;
  ...
}

此外,您还必须修改 MyMethod()

void MyMethod() {
  self.MethodA();
  self.MethodB();
  self.MethodC();
}

还添加一个方法,可以使用Mock对象注入 this.self 字段

public void setMock(BaseClass mock) {
  this.self = mock;
}

现在你可以模仿:

DerivedClass target = new DerivedClass ();
BaseClass  mock = new  Mock(typeof(BaseClass));
target.setMock(mock);
target.MyMethod();

mock.verify(MethodA);
mock.verify(MethodB);
mock.verify(MethodC);

使用此技术,您还可以模拟嵌套方法调用。

答案 3 :(得分:2)

将基类方法包装在方法中并设置该方法 e.g。

public class B : A
{
    public virtual BaseMyMethod(object input)
    {
        // Do something
        base.MyMethod(input);
    }    
public override MyMethod(object input)
    {
        // Do something
        BaseMyMethod(input);
    }
}

现在设置BaseMyMethod

答案 4 :(得分:1)

同意Mark,使用Moq是不可能的。

根据您的情况,您可以考虑swithcing from inheritance to composition。然后,您将能够模拟依赖项并验证您的方法。当然在某些情况下它可能不值得。

答案 5 :(得分:0)

我找到了这个解决方案 - 丑陋但它可以工作。

var real = new SubCoreClass();                        
var mock = new Mock<SubCoreClass>();
mock.CallBase = true;

var obj = mock.Object;

mock
   .Setup(c => c.Execute())
   .Callback(() => 
      {                                                                       
         obj.CallBaseMember(typeof(Action), real, "Execute");             
         Console.WriteLine(obj.GetHashCode());
      }
      );

public static Delegate CreateBaseCallDelegate(object injectedInstance, Type templateDelegate, object instanceOfBase, string methodName)
{
   var deleg = Delegate.CreateDelegate(templateDelegate, instanceOfBase, methodName);
   deleg.GetType().BaseType.BaseType.GetField("_target", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(deleg, injectedInstance);

   return deleg;
}

public static object CallBaseMember(this object injectedInstance, Type templateDelegate, object instanceOfBase, string methodName, params object[] arguments)
{
   return CreateBaseCallDelegate(injectedInstance, templateDelegate, instanceOfBase, methodName).DynamicInvoke(arguments);
}