Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing abstract classes and or interfaces

I'm trying to start using Unit Testing on my current project in Visual Studio 2010. My class structure, however, contains a number of interface and abstract class inheritance relationships.

If two classes are derived from the same abstract class, or interface I'd like to be able to share the testing code between them. I'm not sure how to do this exactly. I'm thinking I create a test class for each interface I want to test, but I'm not sure the correct way to feed my concrete classes into the applicable unit tests.


Update

OK here's an example. Say I have an interface IEmployee , which is implemented by an abstract class Employee, which is then inherited by the two concrete classes Worker and Employee. (Code show below)

Now say I want to create tests that apply to all IEmployees or Employees. Or alternatively create specific tests for specific types of Employees. For example I may want to assert that setting IEmployee.Number to a number less then zero for any implementation of IEmployee throws an exception. I'd prefer to write the tests from the perspective of any IEmployee and then be able to use the tests on any implementation of IEmployee.

Here's another example. I may also want to assert that setting the vacation time for any employee to a value less then zero throws and error. Yet I may also want to have different tests that apply to a specific concrete version of Employee. Say I want to test that Worker throws an exception if they are provided more then 14 days vacation, but a manager can be provided up to 36.

public interface IEmployee
{
    string Name {get; set;}
    int Number {get; set;}
}


public abstract class Employee:IEmploee
{
    string Name {get; set;}
    int Number {get;set;}
    public abstract int VacationTime(get; set;)
}


public abstract class Worker:IEmployee
{
    private int v;

    private int vTime;
    public abstract int VacationTime
    {
       get
       {
         return VTime;
       }
       set
      { 
         if(value>36) throw new ArgumentException("Exceeded allowed vaction");
         if(value<0)throw new ArgumentException("Vacation time must be >0");
         vTime= value;
       }
    }


    public void DoSomWork()
    {
      //Work
    }
}


public abstract class Manager:IEmployee
{
    public abstract int VacationTime
    {
       get
       {
         return VTime;
       }
       set
      { 
         if(value>14) throw new ArgumentException("Exceeded allowed vaction");
         if(value<0)throw new ArgumentException("Vacation time must be >0");
         vTime= value;
       }
    }

  public void DoSomeManaging()
  {
      //manage
  }

}

So I guess what I'm looking for is a work flow that will allow me to nest unit tests. So for example when I test the Manager class I want to first test that it passes the Employee and IEmployee tests, and then test specific members such as DoSomeManaging().

like image 333
Eric Anastas Avatar asked Sep 16 '10 22:09

Eric Anastas


1 Answers

I guess I know what you mean. I had the same issue.

My solution was to create a hierarchy also for testing. I'll use the same example you show.

First, have an abstract test class for the base IEmployee.

It has two main things: i. All the test methods you want. ii. An abstract method that returns the desired instance of the IEmployee.

[TestClass()]
public abstract class IEmployeeTests
{
    protected abstract GetIEmployeeInstance();

    [TestMethod()]
    public void TestMethod1()
    {
    IEmployee target = GetIEmployeeInstance();
    // do your IEmployee test here
    }

}

Second, you have a test class for each implementation of IEmployee, implementing the abstract method and providing appropriate instances of IEmployee.

[TestClass()]
public class WorkerTests : IEmployeeTests
{

    protected override GetIEmployeeInstance()
    {
        return new Worker();
    }

}

[TestClass()]
public class ManagerTests : IEmployeeTests
{

    protected override GetIEmployeeInstance()
    {
        return new Manager();
    }

}

You can see everything works as expected and VS gives you the expected test methods for each WorkerTests and ManagerTests classes in the TestView window. You can run them and have the test results for each implementation of the IEmployee interface, having to create the tests only in the base IEmployeeTests class.

You can always add specific test for the derived WorkerTests and ManagerTests classes.

The question would be now, what about classes that implement multiple interfaces, let's say EmployedProgrammer?

public EmployedProgrammer : IEmployee, IProgrammer
{
}

We don't have multiple inheritance in C#, so this is not an option:

[TestClass()]
public EmployedProgrammerIEmployeeTests : IEmployeeTests, IProgrammerTests
{
     // this doesn't compile as IEmployeeTests, IProgrammerTests are classes, not interfaces
}

For this scenario, a solution is to have the following test classes:

[TestClass()]
public EmployedProgrammerIEmployeeTests : IEmployeeTests
{
    protected override GetIEmployeeInstance()
    {
        return new EmployedProgrammer();
    }
}

[TestClass()]
public EmployedProgrammerIProgrammerTests : IProgrammerTests
{
    protected override GetIProgrammerInstance()
    {
        return new EmployedProgrammer();
    }
}

with

[TestClass()]
public abstract class IProgrammerTests
{
    protected abstract GetIProgrammerInstance();

    [TestMethod()]
    public void TestMethod1()
    {
        IProgrammer target = GetIProgrammerInstance();
        // do your IProgrammerTest test here
    }

}

I'm using this with good results. Hope it helps.

Regards, Jose

like image 137
Jose Avatar answered Nov 05 '22 00:11

Jose