how to properly test an abstract class

前端 未结 2 671
耶瑟儿~
耶瑟儿~ 2021-01-13 07:34

I\'m currently in the process of creating a unit test for an abstract class, called Component. VS2008 compiled my program no problems so I was able to create a

相关标签:
2条回答
  • 2021-01-13 08:17

    You cannot instantiate an abstract class. So you could write a mock implementation of this abstract class (where you should implement the abstract members) in your unit test project and then call the methods you are trying to test. You could have different mock implementations in order to test various methods of your class.

    As an alternative to writing a mock implementation you could use a mock framework such as Rhino Mocks, Moq, NSubstitute, ... which could simplify this task and allow you to define expectations for the abstract members of the class.


    UPDATE:

    As requested in the comments section here's an example.

    Let's suppose that you have the following abstract class that you want to unit test:

    public abstract class FooBar
    {
        public abstract string Foo { get; }
    
        public string GetTheFoo()
        {
            return "Here's the foo " + Foo;
        }
    }
    

    Now in your unit test project you could implement it by writing a derived class implementing the abstract members with mocked values:

    public class FooBarMock : FooBar
    {
        public override string Foo 
        { 
            get { return "bar" } 
        }
    }
    

    and then you could write your unit test against the GetTheFoo method:

    // arrange
    var sut = new FooBarMock();
    
    // act
    var actual = sut.GetTheFoo();
    
    // assert
    Assert.AreEqual("Here's the foo bar", actual);
    

    and with a mock framework (Moq in my example) you do not need to implement this abstract class in the unit test but you could directly use the mocking framework to define expectations of the abstract members that the method under test is relying upon:

    // 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);
    
    0 讨论(0)
  • 2021-01-13 08:21

    This is how I do it. With a inner-nested-class on the UnitTest class.

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

    and the "real" abstract class below

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

    This question is older, but the principals are the same.

    Here are my VS2019, year 2020 .. package imports.

      <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>  
    
    0 讨论(0)
提交回复
热议问题