使用 Moq 模拟基类方法调用

发布于 2024-08-02 08:25:44 字数 126 浏览 2 评论 0原文

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

我想验证传递给我的方法的日期在传递给基类方法时格式是否正确,因此我想最小起订量基类方法调用。起订量可以吗?

I am modifiying a class method which formats some input paramater dates which are subsequently used as params in a method call into the base class (which lives in another assembly).

I want to verify that the dates i pass in to my method are in the correct format when they are passed to the base class method so i would like to Moq the base class method call. Is this possible with Moq?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

空城仅有旧梦在 2024-08-09 08:25:44

截至 2013 年,您可以使用最新的起订量。这是一个示例

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
} 

As of 2013 with latest Moq you can. Here is an 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
} 
べ映画 2024-08-09 08:25:44

如果我正确理解你的问题,你有一个在其他程序集中定义的类 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,虽然它可以重写 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 可能的例外之外,动态模拟库无法执行您无法执行的任何操作手动做。

但是,我假设调用您尝试验证的基本方法会产生一些可观察到的副作用,因此如果可能,您可以使用基于状态的测试而不是基于行为的测试来验证调用该方法的结果吗?

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

If I understand your question correctly, you have a class A defined in some other assembly, and then an class B implemented more or less like this:

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

And now you want to verify that base.MyMethod is called?

I don't see how you can do this with a dynamic mock library. All dynamic mock libraries (with the exception of TypeMock) work by dynamically emitting classes that derive from the type in question.

In your case, you can't very well ask Moq to derive from A, since you want to test B.

This means that you must ask Moq to give you a Mock<B>. However, this means that the emitted type derives from B, and while it can override MyMethod (which is still virtual) and call its base (B.MyMethod), it has no way of getting to the original class and verify that B calls base.MyMethod.

Imagine that you have to write a class (C) that derives from B. While you can override MyMethod, there's no way you can verify that B calls 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
    }
}

Again with the possible exception of TypeMock, dynamic mock libraries cannot do anything that you cannot do manually.

However, I would assume that calling the base method you are trying to verify has some observable side effect, so if possible, can you use state-based testing instead of behaviour-based testing to verify the outcome of calling the method?

In any case, state-based testing ought to be your default approach in most cases.

岁月流歌 2024-08-09 08:25:44

同意马克的观点,使用起订量是不可能的。

根据您的情况,您可以考虑从继承切换到组合。然后您将能够模拟依赖关系并验证您的方法。当然,在某些情况下,这可能不值得。

Agree with Mark, it's not possible using Moq.

Depending on your situation you may consider swithcing from inheritance to composition. Then you'll be able to mock the dependency and verify your method. Of course in some cases it just might not worth it.

缪败 2024-08-09 08:25:44

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

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

,现在设置 BaseMyMethod

wrap the base class method in a method and setup that method
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);
    }
}

and now Setup the BaseMyMethod

对你再特殊 2024-08-09 08:25:44

这很可能是模拟基类。但你必须修改目标类。

对于前。 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();
}

还要添加一个方法,该方法可以注入 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);

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

It is quite possible mocking base class. But you will have to modify target class.

For ex. DerivedClass extends BaseClass.
BaseClass has methods MethodA(), MethodB(), MethodC()...
The DerivedClass has this method:

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

You want to mock base class in order to validate that all MethodA(), MethodB(), MethodC() are being called inside MyMethod().

You have to create a field in the DerivedClass:

class DerivedClass {
  private BaseClass self = this;
  ...
}

And also You have to modify the MyMethod():

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

Also add a method, which can inject the this.self field with Mock object

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

Now you can 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);

Using this technic, you can also mock nested method calls.

瑕疵 2024-08-09 08:25:44

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

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

I found this solution - ugly but it could work.

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文