Pedro Faustino
Pedro Faustino

Reputation: 267

Mock interface method so that all classes that implement that interface use that implementation

I have an interface that is implemented in multiple classes. That interface has a method which returns a bool and I'm trying to mock that method so that it always returns true for unit testing purposes.

Example:

public interface IBase
{
}

public interface IParent
{
    bool IsValid(int number);
    ChildTypeEnum Type { get; }
}

public class A: IBase, IParent
{
    public bool IsValid(int number){...}
    ChildTypeEnum Type { get { return ...; } }
    //other methods
}

public class B: IBase, IParent
{
    public bool IsValid(int number){...}
    ChildTypeEnum Type { get { return ...; } }
    //other methods
}

public class C: IBase, IParent
{
    public bool IsValid(int number){...}
    ChildTypeEnum Type { get { return ...; } }
    //other methods
}

public class D: IBase
{
    //other methods
}

Is there any way to mock the IParent.IsValid() method so that I can invoke the mocked method when trying to test the ChildsEngine.ValidateParent() method?

public class ChildsEngine 
{
    private readonly IChildsLoader childsLoader;

    //Constructor for mocking
    public ScheduleRulesEngine(IChildsLoader childsLoader)
    {
        this.childsLoader = childsLoader;
    }


    public ParentValidation ValidateParent(int number, List<ChildTypeEnum> filterChilds = null)
    {
        var invalidChilds = new List<ChildTypeEnum>();

        var childs = childsLoader.Childs.OfType<IParent>();

        if (filterChilds != null)
            childs = childs.Where(x => filterChilds .Contains(x.Type)).ToList();

        foreach (var child in childsLoader)
        {
            if (!child.IsValid(number))
            {
                invalidChilds.Add(child.Type);
            }
        }

        return new ParentValidation (invalidChilds);
     }
   }

Some extra information:

The only "hack" that I've found is to set the IsValid method as virtual on the child classes, but this forces me to mock the concrete class instead of the interface, meaning that my tests are susceptible to changes if the constructor of the classes suffers changes.

Upvotes: 0

Views: 3168

Answers (2)

pfx
pfx

Reputation: 23224

With Moq you can mock the interface directly.

var parent1 = new Mock<IParent>();
parent1.Setup(m => m.IsValid(It.IsAny<Int32>())).Returns(true);

var parent2 = new Mock<IParent>();
parent2.Setup(m => m.IsValid(It.IsAny<Int32>())).Returns(true);

List<IParent> parents = new List<IParent> { parent1.Object, parent2.Object };

Boolean testResult = ValidateParent(parents, 123);

Edit

In response to your comment below, where the list has to be a List<IBase>.
With Moq an interface can be implemented dynamically.

var parent1 = new Mock<IParent>();
parent1.Setup(m => m.IsValid(It.IsAny<Int32>())).Returns(true);
IBase base1 = parent1.As<IBase>().Object; // Implement the IBase interface.

var parent2 = (new Mock<IParent>());
parent2.Setup(m => m.IsValid(It.IsAny<Int32>())).Returns(true);
IBase base2 = parent2.As<IBase>().Object;

List<IBase> bases = new List<IBase> { base1, base2 };
Boolean testResult = ValidateParent(bases, 123);
Console.WriteLine(testResult);  

Upvotes: 0

John Wu
John Wu

Reputation: 52240

Why wouldn't you just stub the interface?

public Stub : IParent, IBase
{
    public bool IsValid(int number) { get { return true; } }
}


//Arrange
var list = Enumerable.Range(1,10).Select( 
        i => new Stub() as IBase
    ).ToList();
var o = new ClassUnderTest();

//Act
var result = o.ValidateParent(list, 7);

//Assert
Assert.IsTrue(result);

Upvotes: 1

Related Questions