Mocking a base class method call with Moq
Asked Answered
moq
R

6

29

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?

Roommate answered 18/8, 2009 at 10:55 Comment(0)
S
21

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
} 
Sconce answered 15/1, 2014 at 20:42 Comment(1)
Also note that with this Moq setup it is possible to pass constructor arguments to the mocked class [var mockMyViewModel = new Mock<MyViewModel>(new Mock<IMyViewModelConstructorArg>().Object){CallBase = true};]Linnette
T
13

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.

Trebizond answered 18/8, 2009 at 12:32 Comment(0)
P
3

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.

Photodrama answered 27/8, 2009 at 16:4 Comment(0)
S
3

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

Studbook answered 7/4, 2011 at 9:55 Comment(0)
G
3

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.

Gallivant answered 28/12, 2013 at 0:37 Comment(1)
compiler error : keyword 'this' is not available in the current context. In Derived class field.Continuity
T
0

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);
}
Thoroughpaced answered 30/1, 2012 at 12:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.