Делать методы «внутренними» для удаления зависимостей (для модульных тестов) - хорошая практика?Есть ли лучший способ? - PullRequest
5 голосов
/ 02 февраля 2011

У меня есть класс следующим образом.

public class MyClass  
{  
    public MyMethod()  
    {  
      int x = CalculateSomething();  
    }

   private int CalculateSomething()  
   {  
      // Do something and return an int    
      return 100;  
   }  
}  

Для модульного тестирования я добавил [assembly: InternalsVisibleTo("MyTests")] и изменил закрытый метод на внутренний виртуальный.В проекте модульного теста я создал класс MockMyClass и создал закрытый метод следующим образом.

public class MockMyClass : MyClass  
{  
   public bool MadeHappyNoise {get; set;}
   internal override int CalculateSomething()  
   {  
      MadeHappyNoise = true;  
      return base.CalculateSomething();  
   }  
}

Теперь модульный тест выглядит следующим образом:

[TestMethod()]
public void WasCalculateSomethingCalledOK()  
{  
   MockMyClass mk = new MockMyClass();
   mk.MyMethod();  
   Assert.IsTrue(mk.MadeHappyNoise, "Oops...CalculateSomething not called...");  
}  

Несколько вопросов:хороший способ удалить зависимости?Лично я не люблю менять метод с частного на внутренний, но у меня нет выбора (кроме использования Reflection, возможно).Кроме того, атрибут InternalsVisibleTo («MyTests»), находящийся в производственном коде, не подходит.Может кто-нибудь указать мне на лучшее решение, пожалуйста?Спасибо.

Ответы [ 5 ]

1 голос
/ 02 февраля 2011

Слишком много работы за слишком маленькую ценность. Все, что говорит мне этот тест (если он пройдет), это то, что вызов MyMethod вызывает другой закрытый метод. Модульный тест должен проверять поведение, предоставляемое MyMethod () - что должно произойти / изменить после вызова MyMethod? .

Название вопроса также немного вводит в заблуждение - я не вижу никакой проблемы, связанной с зависимостями.

Вам по большей части не нужен InternalsVisibleTo .. просто протестируйте закрытый метод через публичный метод, который его выполняет, например, Напишите модульные тесты для MyMethod (). Кроме того, если вы практикуете программирование на основе тестов, у вас уже есть тесты, которые охватывают каждую строку кода в приватном методе.

1 голос
/ 02 февраля 2011

Можете ли вы изменить его следующим образом:

public class MyClass  
{  
    private Calculator calculator;  

    public myMethod()  
    {  
      int x = calculateSomething();  
    }

    public void SetCalculator( Calculator c ){
        calculator = c;  
    }

   private int calculateSomething()  
   {  
        return calculator.CalculateSomething();
   }  
}  

И затем иметь калькулятор в качестве отдельного класса и установить экземпляр на MyClass

public Class Calculator {

   public virtual int CalculateSomething()  
   {  
      // Do something and return an int    
      return 100;  
   }  
}

Вы можете заставить Калькулятор реализоватьинтерфейс и затем иметь другую реализацию калькулятора или макет, который вы используете в своих тестах.

1 голос
/ 02 февраля 2011

Это скорее зависит от методов, которые вы меняете. unit - это наименьший тестируемый компонент программного обеспечения - он редко означает один тест на метод.

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

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

1 голос
/ 02 февраля 2011

Хм. У меня есть некоторые проблемы с этим кодом, но мы будем делать по одному.

  1. Зачем вам проверять, вызывает ли MyMethod метод CalculateSomething? Это деталь реализации, которая, вероятно, может измениться (что если завтра она вызовет CalculateSomething2, но кроме этого все еще делает то, что должна делать?). Если вы хотите проверить структуру кода MyMethod, сделайте проверку кода, а не модульное тестирование.

  2. Вы говорите, что MyMethod сложен, и вы хотите проверить поток кода внутри. Если внутри несколько путей, вам все равно нужно написать модульный тест для каждого пути, так почему бы вам не проверить результат вызова MyMethod вместо проверки его внутри?

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

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

    Затем вы можете использовать фиктивный фреймворк (например, RhinoMocks ), чтобы высмеивать этот класс. Каркас позволяет вам определить, какие функции вы ожидаете вызывать и в каком порядке и что они должны возвращать.

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

0 голосов
/ 02 февраля 2011

Если это кусок устаревшего кода, который вы слишком напуганы, чтобы потрогать, я бы посоветовал вам создать модульный тест, который бы создавал MyClass.Предварительно создайте открытое свойство в MyClass для предоставления значения x.

В только что созданном модульном тесте утверждают, что значение x равно 100 после создания экземпляра MyClass.Как только вы это сделаете, рефакторинг, как @alb предлагает.Запустите тест снова, убедитесь, что x по-прежнему равен 100, и протестируйте класс калькулятора отдельно и, в конечном итоге, удалите предварительное открытое свойство для x в MyClass.надеюсь, что это поможет.

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