2010-03-29 2 views
3

Я относительно новичок в использовании MSpec, и когда я пишу все больше тестов, становится очевидным, что для уменьшения дублирования вам часто приходится использовать базовый класс для вашей установки в соответствии с Rob Conery's articleПри использовании насмешличной структуры и MSPEC, где вы устанавливаете свои заглушки

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

Есть ли лучший подход, который мне не хватает?

ответ

7

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

В MSpec фаза аранжировки выполняется в полях Establish. Например:

public class When_the_temperature_threshold_is_reached 
{ 
    static ITemperatureSensor Sensor; 
    static Threshold Threshold; 

    Establish context =() => 
     { 
      Sensor = MockRepository.GenerateStub<ITemperatureSensor>(); 
      Sensor 
       .Stub(x => x.GetTemperature()) 
       .Return(42); 

      Threshold = new Threshold(Sensor); 
     }; 

    Because of =() => Reached = Threshold.IsReached(40); 

    It should_report_that_the_threshold_was_reached = 
     () => Reached.ShouldBeTrue(); 
} 

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

public abstract class TemperatureSpecs 
{ 
    protected static ITemperatureSensor CreateSensorAlwaysReporting(int temperature) 
    { 
     var sensor = MockRepository.GenerateStub<ITemperatureSensor>(); 
     sensor 
      .Stub(x => x.GetTemperature()) 
      .Return(temperature); 

     return sensor; 
    } 
} 

public class When_the_temperature_threshold_is_reached : TemperatureSpecs 
{ 
    // Everything else cut for brevity. 
    Establish context =() => 
     { 
      Sensor = CreateSensorAlwaysReporting(42); 

      Threshold = new Threshold(Sensor); 
     }; 
} 

Это дает то преимущество, что вы можете влиять на возвращаемом значение пенька от самого контекста: Вы можете сделать это, сохраняя как можно больше информации, как это возможно локальной в контекст и обеспечить хорошее название для метода «настройки» в базовом классе.

Нет необходимости указывать или ожидать чего-либо, связанного с заглушкой, в Because. Когда запускается Because, ваша система должна находиться в состоянии, когда ее можно осуществлять без дальнейшей подготовки.

+0

Спасибо, Александр, это имеет смысл. Я надеялся избежать установки контекста в каждом производном классе, но я предполагаю, что это не будет возможно. –

+1

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