Сложные классы модульного тестирования с множеством частных методов

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

public class SomeComplexClass
{
    IRepository _repository;

    public SomeComplexClass()
       this(new Repository())
    {
    }

    public SomeComplexClass(IRepository repository)
    {
        _repository = repository;
    }


    public List<int> SomeComplexCalcualation(int option)
    {
        var list = new List<int>();

        if (option == 1)
            list = CalculateOptionOne();
        else if (option == 2)
            list = CalculateOptionTwo();
        else if (option == 3)
            list = CalculateOptionThree();
        else if (option == 4)
            list = CalculateOptionFour();
        else if (option == 5)
            list = CalculateOptionFive();

        return list;
    }

    private List<int> CalculateOptionOne()
    {
        // Some calculation
    }

    private List<int> CalculateOptionTwo()
    {
        // Some calculation
    }

    private List<int> CalculateOptionThree()
    {
        // Some calculation
    }

    private List<int> CalculateOptionFour()
    {
        // Some calculation
    }

    private List<int> CalculateOptionFive()
    {
        // Some calculation
    }
}

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

  • Установите все частные методы на внутренние и используйте [assembly: InternalsVisibleTo()]

  • Выделите все приватные методы в отдельный класс и создайте интерфейс.

  • Сделайте все методы виртуальными и в моих тестах создайте новый класс, наследуемый от этого класса, и переопределите методы.

Есть ли другие варианты тестирования вышеуказанного класса, которые были бы лучше, чем то, что я перечислил?

Если бы вы выбрали один из тех, что я перечислил, можете ли вы объяснить, почему?

Спасибо


person lancscoder    schedule 29.04.2010    source источник
comment
Это не имеет отношения к вашему вопросу, но конструкция if/else кажется мне странной. С точки зрения дизайна, часто ли у вас есть клиенты класса, вызывающие несколько типов вычислений, или один клиент обычно вызывает только одну опцию?   -  person Harper Shelby    schedule 29.04.2010
comment
Это упрощенная версия моего фактического класса, но многие клиенты обычно вызывают много опций.   -  person lancscoder    schedule 29.04.2010


Ответы (5)


Вам не нужно менять интерфейс, чтобы протестировать эти методы. Просто тщательно протестируйте общедоступный интерфейс, чтобы убедиться, что все частные методы протестированы:

 void Test1() 
 {
      new SomeComplexClass(foo).SomeComplexCalcualation(1);
 } 

 void Test2() 
 {
      new SomeComplexClass(foo).SomeComplexCalcualation(2);
 } 

и так далее...

Вы можете использовать инструмент покрытия (например, NCover для .NET), чтобы убедиться, что весь код, который вы хотите протестировать, действительно был испытан.

person Mark Byers    schedule 29.04.2010

Как насчет OptionCalculators, которым исходный класс отправляет работу? У каждого будет только один метод, CalculateOption, и, конечно же, он будет общедоступным и, следовательно, легко тестируемым.

person Carl Manaster    schedule 29.04.2010

Вам нужно только протестировать общедоступные методы класса/интерфейса.

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

person Justin Niessner    schedule 29.04.2010

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

Еще одна вещь, которую следует учитывать: тщательное выполнение одного общедоступного метода проверяет две вещи: а) что код ComputeOptionN работает, И б) что проверка параметров работает правильно.

Не проблема, если вы буквально передаете целые числа, но это не идеально, особенно если сравнение может стать более сложным или если оно может измениться.

person LH.    schedule 29.04.2010

@Карл прав. Это не что иное, как использование шаблона стратегии. Сохраните все calculator в массиве, вставьте массив в SomeComplexClass. Это позволит вам тестировать каждую calculator отдельно, а SomeComplexClass. Теперь вы можете сделать это:

public List<int> SomeComplexCalcualation(int option)
{
     return calculator.find(option);
}

Очень легко издеваться calculator

person Gutzofter    schedule 29.04.2010