5

Let's assume we have the following setup:

public interface IBase
{
    void Foo();
}

public class Base : IBase
{
    public virtual void Foo()
    {
        Console.WriteLine("Called Base.Foo()");
    }
}

public interface IChild : IBase
{
    void Bar();
}

public class Child : Base, IChild
{
    public virtual void Bar()
    {
        Console.WriteLine("Called Child.Bar()");
    }
}

When mocking the Child object everything works fine:

var child = new Mock<Child> { CallBase = true };

child.Object.Bar();
child.Object.Foo();

Output is:

Called Child.Bar()
Called Base.Foo()

But when mocking the IChild interface nothing is printed to the console:

var child = new Mock<IChild> { CallBase = true };

child.Object.Bar();
child.Object.Foo();

Let's assume I can't mock the Child object because there is no parameterless constructor (dependency injection).

I know that I could just do the following:

child.Setup(c => c.Bar()).Callback(() =>
{
    // Copy paste bar-method body
});

child.Setup(c => c.Foo()).Callback(() =>
{
    // Copy paste foo-method body
});

But that would be very ugly.
Is there a clean solution using Mock<IChild>?

samvdst
  • 618
  • 7
  • 22
  • Possible duplicate of [MOQ - how to mock an interface that needs to be cast to another interface?](http://stackoverflow.com/questions/3503974/moq-how-to-mock-an-interface-that-needs-to-be-cast-to-another-interface) – Owen Pauling Jan 18 '17 at 17:35
  • 2
    Mocking is used to simulate a behavior of an object for testing purpose. Why do you even need to mock that object? – meJustAndrew Jan 18 '17 at 17:36
  • Possibly relevant - [Mocking objects with Moq when constructor has parameters](http://stackoverflow.com/questions/7414704/mocking-objects-with-moq-when-constructor-has-parameters) – stuartd Jan 18 '17 at 17:36
  • All an interface does is defines what methods a type must implement. When you mock it, no behavior is defined. YOU have to specify the behavior for your mock by using `Setup` like you seem to not want to do. – D Stanley Jan 18 '17 at 17:41
  • @meJustAndrew Because for testing I need to change the behavior of `Bar()` but not `Foo()` – samvdst Jan 18 '17 at 17:41

2 Answers2

2

As long as you are mocking the interface, you have no access or information about the real classes which explains why you don't get any output (but I guess you understood that).

Unfortunately if you choose to mock an interface (which by definition have no behavior), the only way to make things happen is to Setup the method the way you did.

Another "dirty" way would be to use method extension to your child and base class if the content of the method is only using public attributes and method.

public static class ChildExtension
{
    public static void Bar(this Child child)
    {
        Console.WriteLine("Called Child.Bar()");
    }
}
meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
Bruno Belmondo
  • 2,299
  • 8
  • 18
  • I'll have to go with "copy-paste method body". Thank you for clarification. – samvdst Jan 18 '17 at 18:06
  • What if "copy-paste method body" isn't an option because the method body depends on objects that are injected in the base class? This is also why using the implementation rather than the interface is not possible, because I cannot have a parameterless constructor. – Maurits Moeys Oct 07 '18 at 16:42
  • @MauritsMoeys what you describe is more a design issue rather than a mock issue. It feels like you want to mock some part of your class to test another part. I understand it as : you should break your class in two, mock the "not under tests" part and inject your mock in the class you want to test. You may want to check single responsibility principle for more details. – Bruno Belmondo Oct 08 '18 at 19:51
1

You are going to the wrong direction

Mock exists to help in unit testing. For example if you want to test the method Save() of a class which uses a wrapper over a DbContext like the following:

interface IRepository
{
    void PersistData(object dataToBeSaved);
}

class DataSaver
{
    private IRepository _repository;//this object's method PersistData makes a call to a database
    public DataSaver(IRepository repository)
    {
        _repository = repository;
    }
    public void Save(object dataToBeSaved)
    {
        _repository.PersistData(dataToBeSaved);
    }
}

In this case, in order to test the method Save of the DataSaver you will do a call to it in a unit test, but the problem you will face when doing this is that the method will actually try to save the data using the repository objet. Unless you send a fake repository your unit test will save data every time you run it, and this is not what a unit test should be doing. It should not run a method from a concrete IRepository object, but it should still call it's method.

What you could do in this case to avoid saving of an object is to make another class which implements IRepository only for testing:

class DummyRepository : IRepository
{
    public object DataJustSaved { get; set; }
    public void PersistData(object dataToBeSaved)
    {
        DataJustSaved = dataToBeSaved;
    }
}

Now in your unit test you will do something like this:

var dummyRepository = new DummyRepository(); 
var dataSaver = new DataSaver(dummyRepository);
var savedObject = new Object();
var expectedObject = savedObject;

dataSaver.Save(savedObject);//test the save method

var actualObject = dummyRepository.DataJustSaved;
Assert.AreEqual(expectedObject, actualObject);//verify that the data was passed to the PersistData method

Here the Mock helps

It would be quite difficult to make a fake class for each unit test, that is what alternative mock offers:

var dummyRepository = new Mock<IRepository>();
var dataSaver = new DataSaver(dummyRepository.Object);
var savedObject = new Object();

dataSaver.Verify(x => x.PersistData(savedObject), Times.Once());// just make sure the method PersistData was invoked with the expected data and only once.

The reason Mock exists is to make pretty smart dummies for you, to write unit tests without a great impact but which can reveal bugs, and keep the code doing what only it's supposed to do.

In your case, if you really want to call the actual method of the concrete object:

child.Setup(c => c.Bar()).Callback(() =>
{
    Console.WriteLine("Called Child.Bar()");
});

Then it means that you should not even try to use the mock to reproduce the exact same implementation of the object you mock. What would be the use of the mock if it is doing the same thing as the actual object?

In this case you should remove the mock and create a concrete Child object, as you do not want to simulate the behavior of a child, you are trying to achieve it using a mock which removes the functionality of the mock itself.

The simple answer is to use the concrete object in the unit test:

var child = new Child();

child.Bar();
child.Foo();
meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
  • But what if I have to mock the behavior of `child.Bar()` but not `child.Foo()`? – samvdst Jan 18 '17 at 18:20
  • @soumer I guess you could still mock the Child class `var child = new Mock { CallBase = true };` and setup only the `Bar` method – meJustAndrew Jan 18 '17 at 18:21
  • `new Mock` is not possible because there is no parameterless constructor. Sorry for not making this clear in the example. – samvdst Jan 18 '17 at 18:23
  • 1
    @soumer I am sorry to say this but the answer to question you gave it yourself: you will have to mock only the `IChild` interface, to setup `Bar` do do what you want to do, and setup `Foo` do do what it was doing before. Unless you don't want to change the architecture and make Child an abstract class which defines the method Foo, and declares the method Bar which would be the only method you setup – meJustAndrew Jan 18 '17 at 18:29