C# 如何正确测试抽象类

C# 如何正确测试抽象类,c#,unit-testing,visual-studio-2008,C#,Unit Testing,Visual Studio 2008,我目前正在为一个抽象类创建一个单元测试,名为Component。VS2008编译我的程序没有问题,所以我能够在解决方案中创建一个单元测试项目。不过,我注意到的一点是,在创建测试文件时,有一些方法是我以前从未见过的: internal virtual Component CreateComponent() { // TODO: Instantiate an appropriate concrete class. Component t

我目前正在为一个抽象类创建一个单元测试,名为
Component
。VS2008编译我的程序没有问题,所以我能够在解决方案中创建一个单元测试项目。不过,我注意到的一点是,在创建测试文件时,有一些方法是我以前从未见过的:

internal virtual Component CreateComponent()
        {
            // TODO: Instantiate an appropriate concrete class.
            Component target = null;
            return target;
        }


internal virtual Component_Accessor CreateComponent_Accessor()
        {
            // TODO: Instantiate an appropriate concrete class.
            Component_Accessor target = null;
            return target;
        }
我假设这些都是为了创建一个具体的
组件

在每种试验方法中,都有一条线:

Component target=CreateComponent();//TODO:初始化为适当的值

如何将其初始化为适当的值?或者,我如何实例化一个适当的具体类,正如上面通过
CreateComponent
CreateComponent\u访问器
方法所述

以下是抽象类的构造函数,有关其他信息:


受保护的组件(eVtCompId incomonentid,eLayer inLayerId,IF_SystemMessageHandler inMessageHandler)
您不能实例化抽象类。因此,您可以在单元测试项目中编写这个抽象类的模拟实现(您应该在其中实现抽象成员),然后调用您尝试测试的方法。为了测试类的各种方法,可以使用不同的模拟实现

作为编写模拟实现的替代方案,您可以使用模拟框架,如Rhino Mocks、Moq、NSubstitute等。。。这可以简化此任务,并允许您定义类的抽象成员的期望


更新:

根据评论部分的要求,这里有一个例子

假设您有以下要进行单元测试的抽象类:

public abstract class FooBar
{
    public abstract string Foo { get; }

    public string GetTheFoo()
    {
        return "Here's the foo " + Foo;
    }
}
现在,在单元测试项目中,您可以通过编写一个派生类来实现它,该派生类使用模拟值实现抽象成员:

public class FooBarMock : FooBar
{
    public override string Foo 
    { 
        get { return "bar" } 
    }
}
然后您可以针对
GetTheFoo
方法编写单元测试:

// arrange
var sut = new FooBarMock();

// act
var actual = sut.GetTheFoo();

// assert
Assert.AreEqual("Here's the foo bar", actual);
使用模拟框架(在我的示例中是Moq),您不需要在单元测试中实现这个抽象类,但是您可以直接使用模拟框架来定义被测试方法所依赖的抽象成员的期望:

// arrange
var sut = new Mock<FooBar>();
sut.Setup(x => x.Foo).Returns("bar");

// act
var actual = sut.Object.GetTheFoo();

// assert
Assert.AreEqual("Here's the foo bar", actual);
//排列
var sut=new Mock();
sut.Setup(x=>x.Foo).Returns(“bar”);
//表演
var actual=sut.Object.GetTheFoo();
//断言
AreEqual(“这是foo-bar”,实际值);

我就是这样做的。使用UnitTest类上的内部嵌套类

namespace MyCompany.MyProject.UnitTests
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using FluentAssertions;

    [TestClass]
    [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
    public class MyAbstractClassTests
    {
        [TestMethod]
        public void ConstructorILoggerFactoryIsNullTest()
        {
            Action a = () => new MyUnitTestConcreteClass(null);
            a.Should().Throw<ArgumentNullException>().WithMessage(MyAbstractClass<int>.ErrorMessageILoggerFactoryIsNull);

        }

        [TestMethod]
        public void GetABooleanIsTrueTest()
        {
            /* here is more likely what you want to test..an implemented method on the abstract class */
            Mock<ILoggerFactory> iloggerFactoryMock = this.GetDefaultILoggerFactoryMock();
            MyUnitTestConcreteClass testItem = new MyUnitTestConcreteClass(iloggerFactoryMock.Object);
            Assert.IsTrue(testItem.GetABoolean());
        }   

        [TestMethod]
        public void GetSomeIntsIsNotNullTest()
        {
            /* you may not want to test the abstract methods, but you can */
            Mock<ILoggerFactory> iloggerFactoryMock = this.GetDefaultILoggerFactoryMock();
            MyUnitTestConcreteClass testItem = new MyUnitTestConcreteClass(iloggerFactoryMock.Object);
            Assert.IsNotNull(testItem.GetSomeInts());
        }       


        private Mock<ILoggerFactory> GetDefaultILoggerFactoryMock()
        {
            Mock<ILoggerFactory> returnMock = new Mock<ILoggerFactory>(MockBehavior.Strict);
            ////returnMock.Setup(x => x.SomeBooleanMethod()).Returns(true);
            return returnMock;
        }       



        internal class MyUnitTestConcreteClass : MyAbstractClass<int>
        {
            internal MyUnitTestConcreteClass(ILoggerFactory loggerFactory) : base(loggerFactory)
            {
            }

            public override ICollection<int> GetSomeInts()
            {
                return new List<int> { 111, 222, 333 };
            }
        }
    }
}
名称空间MyCompany.MyProject.UnitTests
{
使用制度;
使用System.Collections.Generic;
使用系统文本;
使用System.Threading.Tasks;
使用Microsoft.VisualStudio.TestTools.UnitTesting;
使用FluentAssertions;
[测试类]
[系统.诊断.代码分析.ExcludeFromCodeOverage]
公共类MyAbstractClassTests
{
[测试方法]
public void ConstructoryFactoryInAllTest()
{
动作a=()=>新的MyUnitTestConcreteClass(null);
a、 Should().Throw().WithMessage(MyAbstractClass.ErrorMessageIlogerFactoryInAll);
}
[测试方法]
公共无效GetABooleanIsTrueTest()
{
/*下面更可能是您想要测试的内容..抽象类上的一个实现方法*/
Mock iloggerFactoryMock=this.GetDefaultILoggerFactoryMock();
MyUnitTestConcreteClass testItem=新的MyUnitTestConcreteClass(iloggerFactoryMock.Object);
Assert.IsTrue(testItem.GetABoolean());
}   
[测试方法]
public void GetSomeIntsIsNotNullTest()
{
/*您可能不想测试抽象方法,但可以*/
Mock iloggerFactoryMock=this.GetDefaultILoggerFactoryMock();
MyUnitTestConcreteClass testItem=新的MyUnitTestConcreteClass(iloggerFactoryMock.Object);
Assert.IsNotNull(testItem.GetSomeInts());
}       
私有Mock GetDefaultILoggerFactoryMock()
{
Mock returnMock=newmock(MockBehavior.Strict);
////returnMock.Setup(x=>x.SomeBooleanMethod()).Returns(true);
返回返回模拟;
}       
内部类MyUnitTestConcreteClass:MyAbstractClass
{
内部MyUnitTestConcreteClass(ILoggerFactory loggerFactory):基(loggerFactory)
{
}
公共覆盖ICollection GetSomeInts()
{
返回新列表{111222333};
}
}
}
}
以及下面的“真实”抽象类

public abstract class MyAbstractClass<T> : where T : struct
{

    public const string ErrorMessageILoggerFactoryIsNull = "ILoggerFactory is null";

    public WhiteListStepBodyAsyncBase(ILoggerFactory loggerFactory)
    {
        if (null == loggerFactory)
        {
            throw new ArgumentNullException(ErrorMessageILoggerFactoryIsNull, (Exception)null);
        }

    }           

    public bool GetABoolean()
    {
          /* note , this is important factor (sometimes), here this implemented method DEPENDS on an abstract method , and why I have the code "return new List<int> { 111, 222, 333 };" above .. see the connection ?? */
                    return this.GetSomeInts().Count > 0;
    }

    public abstract ICollection<int> GetSomeInts();


}
公共抽象类MyAbstractClass:其中T:struct
{
public const string errorMessageILoggerFactoryInAll=“ILoggerFactory为空”;
公共白名单StepBodyAsyncBase(ILoggerFactory loggerFactory)
{
if(null==loggerFactory)
{
抛出新的ArgumentNullException(ErrorMessageIlogerFactoryIsAll,(异常)null);
}
}           
公共图书馆
{
/*注意,这是一个很重要的因素(有时),这里这个实现的方法依赖于一个抽象方法,以及为什么我有上面的代码“returnnewlist{111222333}”;参见连接*/
返回这个.GetSomeInts().Count>0;
}
公共抽象ICollection GetSomeInts();
}
这个问题比较老,但原则是一样的

这是我的VS2019,2020年。。包装进口

  <ItemGroup>
    <PackageReference Include="coverlet.msbuild" Version="2.8.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="FluentAssertions" Version="5.10.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
    <PackageReference Include="Moq" Version="4.13.1" />
    <PackageReference Include="MSTest.TestAdapter" Version="2.0.0" />
    <PackageReference Include="MSTest.TestFramework" Version="2.0.0" />
    <PackageReference Include="coverlet.collector" Version="1.0.1" />
  </ItemGroup>  

全部的
运行时间;建设;本地人;内容文件;分析仪;可传递的

只是为了澄清一下,对于这个抽象类的模拟实现,您的意思是创建一个派生类并实现所有抽象成员……如果您能给我展示一些模拟实现和模拟框架使用的示例,那将非常有帮助(我有Moq框架,用于接口)当然,我有