Модульное тестирование абстрактных классов и / или интерфейсов - PullRequest
8 голосов
/ 17 сентября 2010

Я пытаюсь начать использовать модульное тестирование в моем текущем проекте в Visual Studio 2010. Однако моя структура классов содержит ряд интерфейсных и абстрактных отношений наследования классов.

Если два класса получены изтот же абстрактный класс или интерфейс, который я хотел бы разделить с ними тестовый код.Я не уверен, как именно это сделать.Я думаю, что создаю тестовый класс для каждого интерфейса, который хочу протестировать, но я не уверен, как правильно подать мои конкретные классы в соответствующие модульные тесты.


Обновление

Хорошо, вот пример.Скажем, у меня есть интерфейс IEmployee, который реализуется абстрактным классом Employee, который затем наследуется двумя конкретными классами Worker и Employee.(Код показан ниже)

Теперь скажите, что я хочу создать тесты, применимые ко всем IEmployeees или Employees.Или, альтернативно, создать специальные тесты для конкретных типов сотрудников.Например, я могу утверждать, что установка IEmployee.Number на число меньше нуля для любой реализации IEmployee вызывает исключение.Я бы предпочел написать тесты с точки зрения любого IEmployee, а затем иметь возможность использовать тесты в любой реализации IEmployee.

Вот еще один пример.Я также могу утверждать, что установка времени отпуска для любого сотрудника на значение меньше, чем ноль бросков и ошибка.Тем не менее, я также хочу иметь различные тесты, которые применимы к конкретной конкретной версии Employee.Скажем, я хочу проверить, что Worker выдает исключение, если им предоставляется отпуск продолжительностью более 14 дней, но может быть предоставлен менеджер до 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
  }

}

Так что я думаю, что я ищу эторабочий процесс, который позволит мне вложить модульные тесты.Так, например, когда я тестирую класс Manager, я хочу сначала проверить, что он проходит тесты Employee и IEmployee, а затем протестировать определенные члены, такие как DoSomeManaging().

Ответы [ 5 ]

6 голосов
/ 27 мая 2011

Я думаю, я знаю, что вы имеете в виду. У меня была такая же проблема.

Моим решением было создать иерархию также для тестирования. Я буду использовать тот же пример, что вы показываете.

Сначала создайте абстрактный тестовый класс для базового IEmployee.

У него есть две основные вещи: я. Все методы испытаний, которые вы хотите. II. Абстрактный метод, который возвращает желаемый экземпляр IEmployee.

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

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

}

Во-вторых, у вас есть тестовый класс для каждой реализации IEmployee, реализующий абстрактный метод и предоставляющий соответствующие экземпляры IEmployee.

[TestClass()]
public class WorkerTests : IEmployeeTests
{

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

}

[TestClass()]
public class ManagerTests : IEmployeeTests
{

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

}

Вы можете видеть, что все работает, как ожидалось, и VS предоставляет ожидаемые методы тестирования для каждого класса WorkerTests и ManagerTests в окне TestView. Вы можете запустить их и получить результаты тестов для каждой реализации интерфейса IEmployee, создавая тесты только в базовом классе IEmployeeTests.

Вы всегда можете добавить специальный тест для производных классов WorkerTests и ManagerTests.

Теперь возникает вопрос: а как насчет классов, которые реализуют несколько интерфейсов, скажем, EmployedProgrammer?

public EmployedProgrammer : IEmployee, IProgrammer
{
}

У нас нет множественного наследования в C #, поэтому это не вариант:

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

Для этого сценария решение состоит в том, чтобы иметь следующие тестовые классы:

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

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

с

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

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

}

Я использую это с хорошими результатами. Надеюсь, это поможет.

С уважением, Хосе

0 голосов
/ 11 августа 2011

Желание запустить один и тот же тест для нескольких классов обычно означает, что у вас есть возможность выделить поведение, которое вы хотите протестировать, в один класс (будь то базовый класс или совершенно новый класс, который вы объединяете в существующие классы).

Рассмотрим ваш пример: вместо реализации ограничений на отпуск в Worker и Manager добавьте новую переменную-член в Employee, MaximumVacationDays, установите ограничение в установщике класса сотрудника и проверьтеограничение там:

abstract class Employee {
    private int maximumVacationDays;
    protected Employee(int maximumVacationDays) {
        this.maximumVacationDays = maximumVacationDays
    }
    public int VacationDays {
        set { 
            if (value > maximumVacationDays)
                throw new ArgumentException("Exceeded maximum vacation");
            }
    }
}

class Worker: Employee {
    public Worker(): Employee(14) {}
}

class Manager: Employee {
    public Manager(): Employee(36) {}
}

Теперь у вас есть только один метод для тестирования и меньше кода для обслуживания.

0 голосов
/ 29 июля 2011

Используйте кроты Microsoft для лучшего тестирования. так что вы можете легко высмеивать абстрактный базовый класс / статические методы и т.д. Пожалуйста, обратитесь к следующему сообщению для получения дополнительной информации

объезд-абстрактно-базовые классы-используя-моль

BenzCar benzCar = new BenzCar();    
new MCar(benzCar)
    {
            Drive= () => "Testing"
    }.InstanceBehavior = MoleBehaviors.Fallthrough;

var hello = child.Drive();

Assert.AreEqual("Benz Car driving. Testing", hello);
0 голосов
/ 05 апреля 2011

Похоже, что вы описали «составное модульное тестирование», которое не поддерживается модульными тестами Visual Studio 2010. Такие вещи могут быть сделаны в MbUnit в соответствии с этой статьей . В Visual Studio 2010 можно создать абстрактные тесты , что, вероятно, не совсем то, что вам нужно. Здесь - описание реализации абстрактных тестов в VS (раздел «Пример наследования»).

0 голосов
/ 17 сентября 2010

Я думаю, вы хотите создать модульные тесты для методов в абстрактных классах.

Я не уверен, что имеет смысл протестировать защищенный метод на абстрактном классе, но если вы настаиваете, просто расширьте класс в классе, используемом исключительно для тестирования модулей.Таким образом, вы можете предоставить защищенные методы в абстрактном классе, который вы хотите протестировать, с помощью открытых методов расширяющего класса, которые просто вызывают метод в абстрактном классе.

Если у вас есть методы в абстрактных классах,Я хочу реорганизовать их в отдельные классы, просто выставить их как публичные методы и протестировать.Попробуйте взглянуть на свое дерево наследования с точки зрения «первого теста», и я уверен, что вы также найдете это решение (или подобное).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...