C# 单元测试继承

C# 单元测试继承,c#,unit-testing,tdd,C#,Unit Testing,Tdd,我有一个关于单元测试的问题。假设我有几个类继承父类的行为。我不想测试所有的孩子们的这种行为。相反,我将测试父类。然而,我还应该提供一个测试,证明这种行为在儿童课堂上是可行的。你认为像Assert.IsTrue(new ChildClass()是ParentClass)这样的东西有意义吗?我认为编写一个继承有效的测试是浪费时间。如果您尝试使用基类方法,则编译器将检查它们是否可用,前提是您不使用intellisense。我可能会在一个子类中测试行为,然后只在每个修改行为的子类(或行为所依赖的某些状态

我有一个关于单元测试的问题。假设我有几个类继承父类的行为。我不想测试所有的孩子们的这种行为。相反,我将测试父类。然而,我还应该提供一个测试,证明这种行为在儿童课堂上是可行的。你认为像Assert.IsTrue(new ChildClass()是ParentClass)这样的东西有意义吗?

我认为编写一个继承有效的测试是浪费时间。如果您尝试使用基类方法,则编译器将检查它们是否可用,前提是您不使用intellisense。我可能会在一个子类中测试行为,然后只在每个修改行为的子类(或行为所依赖的某些状态)中测试行为。

您不想测试对象的类型,除非它来自非类型化工厂方法。否则,您将针对C#编译器编写一个单元测试,而这不是您想要做的。

C#编译器将为您处理此类检查

如果你喜欢,你可以写以下内容:

ParentClass childClass = new ChildClass()
var testOutput = childClass.ParentMethod();
Assert.IsNotNull(testOutput);

有两种方法可用于测试基类的行为:

  • 创建基类的存根实现,并对存根进行单元测试。只测试公共方法。测试私有和受保护的方法是毫无意义的,因为这些方法将被应该在子类中测试的公共方法使用。这种方法不会强制要求基类的实现没有错误地跟踪行为
  • 为执行基类方法的单元测试创建测试超类。无论何时测试基类的子类,都要让测试类从测试超类继承。这种方法确保不会无意中更改基类的行为,但会限制测试的灵活性

  • 不必费心验证继承是否真的有效(整个
    Assert.IsTrue(new ChildClass()就是ParentClass)
    事情)。您应该只测试行为。这是.Net framework(继承)的一个结构特性,您应该相信它是有效的,否则您将发现自己处于检查框架特性的恶性循环中。

    如果您使用的是最先进的单元测试框架,我不理解这句话

    我不想测试所有的孩子们的这种行为

    如果您将子类的实例交给单元测试(针对父类的实例编写),则单元测试(针对父类的实例编写)应能正常工作,前提是子类没有以破坏继承方法契约的方式重写父类行为的某个方面。这正是你需要测试的,不是吗


    如果您担心运行测试所需的时间,我会仔细检查以确保您的测试是分区的,这样您就可以根据您正在积极工作的内容有选择地运行测试(但仍然定期运行完整的公文包以捕获意外的依赖项)。

    使您的测试从基类测试继承,大概是这样的

    public class MyBaseClass
    {
        public virtual void DoStuff() { }
        public virtual void DoOtherStuff() { }
    }
    
    public class MySubClass : MyBaseClass
    {
        public override void DoOtherStuff()
        {
            // different to to base
        }
    }
    
    public abstract class TestOfBaseClass
    {
        protected abstract MyBaseClass classUnderTest { get; }
    
        [TestMethod]
        public void TestDoStuff()
        {
            classUnderTest.DoStuff();
            Assert.StuffWasDone();
        }
    }
    
    [TestClass]
    public class WhenSubclassDoesStuff : TestOfBaseClass
    {
        protected override MyBaseClass classUnderTest
        {
            get { return new MySubClass(); }
        }
    
        [TestMethod]
        public void ShoudDoOtherStuff()
        {
            classUnderTest.DoOtherStuff();
            Assert.OtherStuffDone();
        }
    }
    
    大多数流行的测试框架在运行子类测试时都会在基本测试中运行测试方法


    或者看看类似的东西,我的观点是测试结构应该反映对象结构 所以如果你有一辆继承自汽车的汽车

    然后您应该拥有从TestVehicle类继承的TestCar类

    通过执行此测试,车辆自动继承所有车辆测试的正确测试,这意味着如果该功能在车辆中被超控,则很可能会中断测试,突出显示需要在车辆中进行超控测试以支持新行为,如果测试没有被破坏,那么覆盖首先可能是多余的

    乙二醇


    注意:仅当测试继承的对象时才应使用继承,而不仅仅是因为您希望对10个不相关的对象执行相同的测试。如果你想这样做,我建议你使用一个静态助手类。这是一个很好的理由来实际测试“继承是否有效”(即符合Liskov替换原则),而不仅仅是验证子类是否是父类的子类型。我并不关心运行测试所需的时间。我只想写尽可能少的测试,以便将来更容易地进行测试。@trendl-如果使用NUnit,可以为基类创建一个单元测试并使用[TestCase]属性来输入你所有的子类-这让你可以按照Joel的建议免费检查你所有的子类。有一些帖子批评你在测试类上使用继承,比如说我真的同意你,我认为这些帖子看到人们滥用继承,所以鼓吹不要使用它单元测试的定义是测试尽可能最小的组件,因此单个方法和属性意味着您应该始终单独测试子调用,而不是说您应该正确地使用它。您可以争辩说,测试继承链没有被破坏是一种有效的测试。毕竟,你可以有一个A类继承B类,B类继承C类,在未来的某个时刻,开发人员决定A直接指向C会更好,而不需要意识到A依赖于B执行的数据转换。这正是我们想要的。谢谢。
    编写继承有效的测试是浪费时间。编译器将检查……
    单元测试捕获的问题和编译器捕获的ISUE是完全独立的。单元测试需要编译才能成功
    如果您尝试使用基类方法,编译器将检查它们是否可用
    问题不在于是否符合基类方法
    class Vehicle
    {
        public virtual bool LicenceRequired
        {
            get{throw new NotImplmentedException()
        }
    }
    class Bicycle:Vehicle
    {
        public override bool LicenceRequired
        {
            get{return false;}
        }
    }
    class Car:Vehicle
    {
        public override bool LicenceRequired
        {
            get{return true;}
        }
    }
    class TestVehicle
    {
        public virtual Void LicenceRequiredTest()
        {
            Try
            {
                LicenceRequired
                Assert.Fail();
            }
            Catch(){}
        }
    }
    class TestBicycle:TestVehicle
    {
        public override void LicenceRequiredTest()
        {
            Assert.IsFalse(LicenceRequired);
        }
    }
    class TestCar:TestVehicle
    {
        public override void LicenceRequiredTest()
        {
            Assert.IsTrue(LicenceRequired);
        }
    }