使用Moq模拟基类方法调用

29

我正在修改一个类方法,该方法格式化一些输入参数日期,并将其用作另一个程序集中基类的方法调用的参数。

我想要验证我传递给我的方法的日期在传递给基类方法时是否处于正确的格式,因此我希望使用Moq模拟基类方法调用。这是否可能?

6个回答

21

截至2013年,使用最新版的Moq你可以这样做。这里有一个示例

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
} 

还要注意,使用这个 Moq 设置可以将构造函数参数传递给模拟类 [var mockMyViewModel = new Mock<MyViewModel>(new Mock<IMyViewModelConstructorArg>().Object){CallBase = true};]。 - brenthompson2

13

如果我理解你的问题正确,你有一个在其他程序集中定义的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 是否调用了base.MyMethod

想象一下,您需要编写一个从 B 派生的类(C)。虽然您可以重写 MyMethod,但无法验证 B 是否调用了 A:

public class C : B
{
    public override MyMethod(object input)
    {
        // How to verify that base calls its base?
        // base in this context means B, not A
    }
}

除了 TypeMock 可能有一些例外,动态 mock 库不能做任何你不能手动完成的事情。

然而,我会假设调用您要验证的基本方法具有一些可观察的副作用,因此如果可能的话,可以使用基于状态的测试来验证调用该方法的结果,而不是行为测试。

无论如何,在大多数情况下,状态测试应该是您的默认方法。


3

将基类方法包装在一个方法和设置该方法中,例如:

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

现在设置BaseMyMethod


3

在it技术中很可能会出现模拟基类的情况。但您需要修改目标类。

例如,DerivedClass 继承自 BaseClassBaseClass 包含方法 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();
}

还需要添加一个方法,可以将this.self字段注入为Mock对象。
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);

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

编译器错误:在派生类字段中,关键字“this”不可用于当前上下文。 - Morgeth888

3

同意Mark的看法,使用Moq是不可能实现的。

根据您的情况,您可以考虑 从继承转换为组合。然后,您就可以模拟依赖项并验证您的方法。当然,在某些情况下,这可能不值得。


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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接