ТЛ; др
В Pivotal мы написали Cedar, потому что мы используем и любим Rspec в наших проектах Ruby. Cedar не предназначен для замены или конкуренции с OCUnit; Он предназначен для того, чтобы предоставить возможность тестирования в стиле BDD в Objective C, так же как Rspec впервые провёл тестирование в стиле BDD в Ruby, но не устранил Test :: Unit. Выбор одного или другого - в значительной степени вопрос стилевых предпочтений.
В некоторых случаях мы разработали Cedar для преодоления некоторых недостатков в том, как OCUnit работает для нас. В частности, мы хотели иметь возможность использовать отладчик в тестах, запускать тесты из командной строки и в сборках CI и получать полезный текстовый вывод результатов тестов. Эти вещи могут быть более или менее полезными для вас.
Длинный ответ
Выбор между двумя средами тестирования, такими как Cedar и OCUnit (например), сводится к двум вещам: предпочтительный стиль и простота использования. Я начну со стиля, потому что это просто вопрос мнения и предпочтения; простота использования имеет тенденцию быть набором компромиссов.
Соображения стиля выходят за рамки того, какую технологию или язык вы используете. Модульное тестирование в стиле xUnit существует гораздо дольше, чем тестирование в стиле BDD, но последнее быстро завоевало популярность, в основном благодаря Rspec.
Основным преимуществом тестирования в стиле xUnit является его простота и широкое применение (среди разработчиков, которые пишут модульные тесты); почти на любом языке, на котором вы могли бы написать код, есть фреймворк в стиле xUnit.
Фреймворки в стиле BDD, как правило, имеют два основных различия по сравнению со стилем xUnit: как вы структурируете тест (или спецификации) и синтаксис для написания ваших утверждений. Для меня структурная разница является основным отличием. Тесты xUnit являются одномерными, с одним методом setUp для всех тестов в данном классе тестов. Однако классы, которые мы тестируем, не являются одномерными; нам часто нужно тестировать действия в нескольких разных, потенциально противоречивых контекстах. Например, рассмотрим простой класс ShoppingCart с методом addItem: (для целей этого ответа я буду использовать синтаксис Objective C). Поведение этого метода может отличаться, когда корзина пуста по сравнению с тем, когда корзина содержит другие предметы; может отличаться, если пользователь ввел код скидки; может отличаться, если указанный элемент может быть отправлены выбранным способом доставки; и т. д. Поскольку эти возможные условия пересекаются друг с другом, вы получаете геометрически растущее число возможных контекстов; в тестировании в стиле xUnit это часто приводит к множеству методов с именами, такими как testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. Структура фреймворков в стиле BDD позволяет вам организовывать эти условия индивидуально, что, на мой взгляд, облегчает обеспечение охвата всех случаев, а также облегчает поиск, изменение или добавление отдельных условий. Например, используя синтаксис Cedar, приведенный выше метод будет выглядеть следующим образом: в тестировании в стиле xUnit это часто приводит к множеству методов с именами, такими как testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. Структура фреймворков в стиле BDD позволяет вам организовывать эти условия индивидуально, что, на мой взгляд, облегчает обеспечение охвата всех случаев, а также облегчает поиск, изменение или добавление отдельных условий. Например, используя синтаксис Cedar, приведенный выше метод будет выглядеть следующим образом: в тестировании в стиле xUnit это часто приводит к множеству методов с именами, такими как testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. Структура фреймворков в стиле BDD позволяет вам организовывать эти условия индивидуально, что, на мой взгляд, облегчает обеспечение охвата всех случаев, а также облегчает поиск, изменение или добавление отдельных условий. Например, используя синтаксис Cedar, приведенный выше метод будет выглядеть следующим образом:
describe(@"ShoppingCart", ^{
describe(@"addItem:", ^{
describe(@"when the cart is empty", ^{
describe(@"with no discount code", ^{
describe(@"when the shipping method applies to the item", ^{
it(@"should add the item to the cart", ^{
...
});
it(@"should add the full price of the item to the overall price", ^{
...
});
});
describe(@"when the shipping method does not apply to the item", ^{
...
});
});
describe(@"with a discount code", ^{
...
});
});
describe(@"when the cart contains other items, ^{
...
});
});
});
В некоторых случаях вы найдете контексты, которые содержат те же наборы утверждений, которые вы можете СУХОЙ, используя общие примеры контекстов.
Второе основное отличие между фреймворками в стиле BDD и фреймами в стиле xUnit, синтаксис утверждений (или совпадений), просто делает стиль спецификаций несколько приятнее; некоторым это действительно нравится, другим нет.
Это приводит к вопросу о простоте использования. В этом случае каждая структура имеет свои плюсы и минусы:
OCUnit существует намного дольше, чем Cedar, и интегрируется непосредственно в Xcode. Это означает, что сделать новую цель тестирования просто, и, в большинстве случаев, запуск и запуск тестов "просто работает". С другой стороны, мы обнаружили, что в некоторых случаях, например, при работе на устройстве iOS, заставить работать тесты OCUnit практически невозможно. Настройка спецификаций Cedar требует больше работы, чем тесты OCUnit, поскольку вы сами получаете библиотеку и ссылаетесь на нее (никогда не бывает тривиальной задачей в XCode). Мы работаем над упрощением настройки, и любые предложения приветствуются.
OCUnit запускает тесты как часть сборки. Это означает, что вам не нужно запускать исполняемый файл для запуска ваших тестов; если какие-либо тесты не пройдены, ваша сборка будет неудачной. Это делает процесс запуска тестов на один шаг проще, и вывод теста напрямую попадает в окно вывода вашей сборки, что облегчает его просмотр. Мы решили встроить спецификации Cedar в исполняемый файл, который вы запускаете отдельно по нескольким причинам:
- Мы хотели иметь возможность использовать отладчик. Вы запускаете спецификации Cedar точно так же, как любой другой исполняемый файл, поэтому вы можете использовать отладчик таким же образом.
- Мы хотели, чтобы в тестах было легко войти в консоль. Вы можете использовать NSLog () в тестах OCUnit, но вывод идет в окно сборки, где вам нужно развернуть шаг сборки, чтобы прочитать его.
- Мы хотели, чтобы отчет о тестировании был легко читаем как в командной строке, так и в Xcode. Результаты OCUnit хорошо отображаются в окне сборки в Xcode, но сборка из командной строки (или как часть процесса CI) приводит к тому, что результаты теста смешиваются с большим количеством других результатов сборки. С раздельными фазами сборки и запуска Cedar разделяет выходные данные, поэтому их легко найти. Тестовый кедр по умолчанию копирует стандартный стиль печати "." для каждой передаваемой спецификации «F» для ошибочной спецификации и т. д. Cedar также имеет возможность использовать настраиваемые объекты-репортеры, так что вы можете получить результаты, полученные любым способом, без особых усилий.
OCUnit является официальной платформой модульного тестирования для Objective C и поддерживается Apple. У Apple практически неограниченные ресурсы, поэтому, если они захотят что-то сделать, это будет сделано. И, в конце концов, это песочница Apple, в которую мы играем. Однако оборотная сторона этой монеты заключается в том, что Apple получает порядка баджиллиона запросов на поддержку и отчетов об ошибках каждый день. Они замечательно справляются со всеми, но могут не справиться с проблемами, о которых вы сообщаете немедленно или вообще. Cedar намного новее и менее выпечен, чем OCUnit, но если у вас есть вопросы, проблемы или предложения, отправьте сообщение в список рассылки Cedar (cedar-discuss@googlegroups.com), и мы сделаем все возможное, чтобы помочь вам. Кроме того, не стесняйтесь раскошелиться на код из Github (github.com/pivotal/cedar) и добавить то, что, по вашему мнению, отсутствует.
Выполнение тестов OCUnit на устройствах iOS может быть затруднено. Честно говоря, я не пробовал это в течение достаточно долгого времени, так что, возможно, стало легче, но в прошлый раз я просто не мог заставить OCUnit-тесты работать с какой-либо функциональностью UIKit. Когда мы писали Cedar, мы убедились, что можем тестировать UIKit-зависимый код как на симуляторе, так и на устройствах.
Наконец, мы написали Cedar для модульного тестирования, что означает, что он не совсем сопоставим с такими проектами, как UISpec. Прошло довольно много времени с тех пор, как я пытался использовать UISpec, но я понял, что он сосредоточен в основном на программном управлении пользовательским интерфейсом на устройстве iOS. Мы специально решили не пытаться заставить Cedar поддерживать эти типы спецификаций, поскольку Apple (в то время) собиралась анонсировать UIAutomation.