模拟接口方法,以便实现该接口的所有类都使用该实现

时间:2018-06-21 21:38:50

标签: c# unit-testing mocking moq

我有一个在多个类中实现的接口。该接口有一个返回布尔值的方法,我正在尝试模拟该方法,以便为进行单元测试而始终返回true。

示例:

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
}

是否可以模拟IParent.IsValid()方法,以便在尝试测试ChildsEngine.ValidateParent()方法时可以调用模拟方法?

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

一些额外的信息:

  • 我有多个子类,但并非所有子类都实现IParent接口
  • IChildsLoader返回的子级列表包括实现IBase的任何子级
  • 在单元测试中,我根据ChildsEngine.ValidateParent()方法返回的ParentValidation进行断言(例如,仅考虑过滤子元素时进行验证)

我发现的唯一“ hack”是在子类上将IsValid方法设置为虚拟方法,但这迫使我模拟具体的类而不是接口,这意味着如果类的构造函数会发生变化。

2 个答案:

答案 0 :(得分:1)

您为什么不只对界面进行存根?

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

答案 1 :(得分:0)

使用Moq,您可以直接模拟接口。

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

修改

为回应您在下面的评论,列表必须为List<IBase>
借助Moq,可以动态实现接口。

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