代码之家  ›  专栏  ›  技术社区  ›  Larry Hipp

LINQ中的单元测试虚拟方法

  •  2
  • Larry Hipp  · 技术社区  · 16 年前

    我有以下需要单元测试的类结构:

    public interface IFoo
    {
        int Value { get;}
        int GetValue();
    }
    
    public class BaseClass : IFoo
    {
        public virtual int Value { get { return 100; } }
        public virtual int GetValue()
        {
            return Value;
        }
    }
    
    public class ChildClass : BaseClass, IFoo
    {
        public override int GetValue()
        {
            return Value;
        }
    }
    

    我正在尝试用Rhinomocks测试childClass.getValue()方法。

    我有以下存根代码:

    public ChildClass CreateChildClass(int value)
    {
       var childClass = MockRepository.GenerateStub<ChildClass>();
       childClass.Stub(x => x.Value).Return(value);
       return childClass;            
    }
    

    以及以下单元测试代码:

    public IEnumerable<IFoo> CreateList()
    {
       yield return CreateChildClass(1000);
       yield return CreateChildClass(2000);
    }
    
    [TestMethod]
    public void virtualMethodTest()
    {
       var list = CreateList();
       var query = from p in list
                   select p.GetValue(); //I can't use p.Value
    
       var sum = query.Sum(p => p);
     }
    

    目前的问题是总和总是零。我可以快速查看列表,并从createList()中看到两个具有正确值的模拟对象。

    是否可以在子类上对虚拟方法进行单元测试?或者我需要使用不同的犀牛模型方法吗?

    谢谢

    1 回复  |  直到 16 年前
        1
  •  2
  •   KeithS    16 年前

    有两件事:

    • 您创建的是具体类的存根,而不是ifoo。模拟在其内部工作中具有高度的反射性,因此多态调用的行为可能相同,也可能不同,这取决于框架和调用的确切性质。
    • 您正在创建存根,根据其性质,存根将忽略对其接口进行的任何不期望的调用(返回返回类型的默认值)。这是问题的直接原因;不需要getValue(),因此它返回默认整数值(0)。

    您希望测试实际类的某些行为,但要覆盖某些其他行为(副作用,而不是测试范围的一部分等)。Rhinomocks中的基本答案是一个partialBlock,它将默认为实际类的行为,除非您希望进行某个调用。因此,创建mockrepository的实例并实例化一个新的mockrepository.partialmock(),而不是generatestub<>。期望您想要覆盖的内部行为,但不要触摸任何其他内容。您可能必须使用较旧的录制和回放方法;当您实例化一个partialBlock,然后在实例上调用expect()时,我通常使用的Rhinomock版本的行为不正确。

    变化将沿着以下几条线:

    //instantiate Mocks in class scope, as an instance of MockRepository
    
    public ChildClass CreateChildClass(int value)
    {
       var childClass = Mocks.PartialMock<ChildClass>();
       childClass.Expect(x => x.Value).Return(value);
       return childClass;            
    }