2013-02-14 4 views
3

При написании тестов MSpec BDD я столкнулся с сценарием, в котором тест, который я ожидал сбой, проходил, но только когда я запускал все свои тесты. Когда я проводил тест по отдельности, он потерпел неудачу, как ожидалось. После некоторого расследования я обнаружил, что какое-то состояние, которое было установлено в предыдущем тесте, не было сброшено до того, как было выполнено второе испытание, и это привело к тому, что второй тест прошел, когда я ожидал, что он потерпит неудачу. Следующий надуманный код воспроизводит сценарий:Как состояние статического объекта совместно используется/устанавливается между контекстами MSpec?

public class ContextBase 
{ 
    protected static object state; 
} 

public class Context_a : ContextBase 
{ 
    Establish context =() => { state = new object(); }; 

    It should_set_state =() => state.ShouldNotBeNull(); 
} 

public class Context_b : ContextBase 
{ 
    Establish context =() => { }; 

    It should_set_state =() => state.ShouldNotBeNull(); 
} 

Оба эти тестового прохода, поскольку Context_a выполняется перед Context_b и в то время Context_B выполняется состояние, которое было установлено в Context_A еще установлен. Если вы запускаете Context_B в изоляции, тогда тест завершается с ошибкой, поскольку состояние не установлено. Интересно, что если вы удалите пустой оператор Set из Context_B, Context_B всегда будет терпеть неудачу, как ожидалось.

Я относительно новичок в MSpec, и это поведение меня удивило. Я предположил, что подобное состояние будет сброшено между выполнением каждого контекста. Возможно, я что-то пропустил ... Правильно ли я строю эти тесты? Если MSpec не сбрасывает состояние, подобное этому между контекстами, автоматически, какую стратегию я должен использовать, чтобы обеспечить сброс состояния в случаях, подобных тому, что приведен в моем примере? Должен ли я помещать метку «Установить лямбда» в класс ContextBase, который устанавливает все поля состояния равными нулю?

ответ

2

MSpec не «перезагружает» статическое состояние между исполняемыми контекстами. Он подчиняется поведению, которое вы знаете, от обычных статических переменных, т. Е. Они не получают повторной инициализации на время запуска приложения (т. Е. Тестового прогона), если вы не сделаете это вручную. Лучше всего инициализировать все поля в Establish каждого контекста.

Другой вариант - добавить дополнительный Establish в ваш базовый класс, но это скроет важную информацию из вашего контекста - вам нужно будет перейти к базовому классу, чтобы увидеть, что поле было инициализировано с определенным значением. Но DRY не относится к испытаниям вообще: я предпочитаю иметь базовые классы с методами protected static, которые я вызываю из производных контекстов (см. Пример this answer).

+0

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

+0

Эй, Джон. Чтобы уточнить комментарий Александра по DRY, исполняемые спецификации предназначены для моделирования данного взаимодействия с системой. Все в порядке, чтобы определить детали вспомогательной настройки, но факторизация сотрудничества, которые уместны для понимания данного взаимодействия, приводит к неясным испытаниям. Для дальнейшего ознакомления см. Https://lostechies.com/derekgreer/2011/07/19/effective-tests-avoiding-context-obscurity/?preview=true&preview_id=514&preview_nonce=7b4ddbe974 –