2010-05-21 2 views
2

Предположим, что я хочу написать единичный тест для проверки определенной функциональности, реализованной в рамках метода. Если бы я хотел полностью выполнить этот метод, мне пришлось бы выполнить дополнительную работу по настройке (ожидание ожиданий объектов и т. Д.). Вместо этого я использую следующий подход:
- Я настроил ожидания, которые я заинтересован в проверке, а затем сделаю протестированный метод выдающим специальный тип исключения (например, TerminateTestException).
- Далее в модульном тесте я обнаруживаю исключение и проверяю ожидания макета.Выбрасывание специального типа исключения для завершения теста на единицу измерения

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

EDIT:
Чтобы уточнить, я не изменяю код SUT. Что я делаю, я предоставляю макет объекта или переопределяю класс SUT, чтобы SUT завершил выполнение после того, как часть, которая мне интересна, выполнена.

private class TestCalculationService : CalculationService 
    { 
     public bool ValidateForSyncCalled; 

     protected override void ValidateForSyncCall() 
     { 
      ValidateForSyncCalled = true; 
      throw new ExceptionToTerminateTest(); 
     }     
    } 


    [TestMethod] 
    public void CalculationService_Calculate_Sync_Calls_ValidateForSyncCall() 
    { 
     InitializeMocks(); 
     TestCalculationService calculationService = new TestCalculationService(); 
     calculationService.MessageInstanceFactory = _mockMessageInstanceFactory; 

     try 
     { 
      calculationService.Calculate(null); 
      Assert.Fail("Exception should have been thrown"); 
     } 
     catch (ExceptionToTerminateTest) 
     { 
      //ok 
     } 

     Assert.IsTrue(calculationService.ValidateForSyncCalled); 
    } 

ответ

3

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

0

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

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

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

if (IsTesting) 
{ 
    throw new TerminateTestException(); 
} 

Тогда можно утверждать, что код, который вы тестируете отличается от кода, который фактически запускаемым. В этом случае вы действительно что-то проверяете?

И один, последний, подумал: я предполагаю, что вы настраиваете все макеты для хотя бы одного тестового примера (случай, который проходит весь путь, или тестовый пример, чтобы исключить не брошенный, когда этого не должно быть). Если вы это делаете, возможно, вам не хватает возможностей рефакторинга в самом тестовом классе. Вам не нужно устанавливать свои макетные объекты для каждого теста индивидуально.

0

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

Смежные вопросы