Почему разработка по контракту не так популярна по сравнению с разработкой через тестирование?

Вы можете подумать, что этот вопрос похож на этот вопрос, заданный ранее в StackOverflow. Но я стараюсь смотреть на вещи по-другому.

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

Если вы следите слово за словом в книге Бертрана Мейерса «Построение объектно-ориентированного программного обеспечения, у самого класса есть внутренние и внешние контракты, так что он делает только то, что должен делать, и ничего не делает». еще. Никаких внешних тестов не требуется, потому что код, обеспечивающий соблюдение контракта, является частью класса.

Быстрый пример, чтобы прояснить ситуацию

TDD

  1. Создайте тест, чтобы убедиться, что во всех случаях значение находится в диапазоне (0–100).

  2. Создайте класс, содержащий метод, который проходит тест.

DBC

  1. Создайте класс, создайте контракт для этого члена var в диапазоне от (0-100), установите контракт на нарушение контракта, определите метод.

Мне лично нравится подход DBC.


Есть ли причина, по которой чистый DBC не так популярен? Языки, инструменты или гибкость, или мне просто нравится, когда код отвечает за себя?

Если вы думаете, что я ошибаюсь, я более чем готов учиться.


person Perpetualcoder    schedule 26.01.2009    source источник


Ответы (9)


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

В TDD тесты пишутся человеком на основе текущих спецификаций метода с естественным текстом, которые (надеюсь) хорошо документированы. Таким образом, человек интерпретирует правильность, написав тест, и получает некоторую уверенность на основе этой интерпретации.

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

person Uri    schedule 26.01.2009
comment
У Эйфеля много интересных идей, но он еще не готов для массового использования. Несколько моих друзей работают над формализмами DBC для Java, что довольно круто. Я работаю над улучшением документации, формализуя директивы - person Uri; 27.01.2009
comment
Статический анализ специфики контрактов также не работает для Eiffel, он имеет только именную проверку типов, действительно хорошую реализацию, но не специфичную для контракта. - person Ryan; 27.01.2009
comment
Документация БУДЕТ устаревшей. Разработчики не будут его читать (возможно, потому, что он, скорее всего, устарел). Тесты, которые нужно проводить отдельно, даже в этом случае игнорировать. Предварительные / пост-условия и инварианты DbC выполняются inline, при каждом вызове, без возможности обхода. - person Marcin; 14.06.2020
comment
Если вы используете Python, вы можете использовать github.com/Parquery/icontract для определения контрактов и автоматического создания тестов. с помощью github.com/mristin/icontract-hypothesis или выполните статический анализ с помощью github.com/pschanely/CrossHair. Итак, мы приближаемся к Python. - person marko.ristin; 03.02.2021

TDD и DbC - две разные стратегии. DbC допускает отказоустойчивость во время выполнения, в то время как TDD действует «во время компиляции» (точнее, он добавляет новый шаг сразу после компиляции для запуска модульных тестов).

Это большое преимущество TDD перед DbC: он позволяет получать обратную связь раньше. Когда вы пишете код методом TDD, вы получаете код и его модульные тесты одновременно, вы можете проверить, что он «работает» в соответствии с тем, что вы считали нужным, что вы закодировали в тесте. С DbC вы получаете код со встроенными тестами, но вам все равно придется его тестировать. ИМО, это, безусловно, одна из причин того, что Dbc не так популярен.

Другие преимущества: TDD создает набор автоматических тестов, которые позволяют обнаруживать (предотвращать чтение) регрессии и выполнять рефакторинг safe, чтобы вы могли постепенно расширять свой дизайн. DbC не предлагает такой возможности.

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

person philant    schedule 27.01.2009
comment
Я нашел DbC полезным при создании прототипов, где у меня нет четкого представления о модели данных и бизнес-правилах, поэтому я вношу большие изменения в код, ища работоспособный дизайн приложения. В этой ситуации TDD замедлит поиск хорошего дизайна. DbC не тормозит изменение дизайна. Приложение, которое я разрабатываю, выполняет сложные задачи с базами данных, файлами и метаданными в файлах. У меня возникли бы проблемы с использованием TDD из-за того, что сложно выделить код, который обычно считывает данные в реальном времени для модульного тестирования. - person AndrewM; 30.10.2015
comment
Не уверен, как DbC допускает отказоустойчивость во время выполнения больше, чем TDD, поскольку контракты теоретически должны обеспечивать более полную проверку во время компиляции, охватывая все возможные случаи, тогда как модульные тесты охватывают только несколько жестко запрограммированных случаев. во время компиляции. - person AjaxLeung; 14.06.2019

Прежде всего, я инженер-программист Eiffel, поэтому могу рассказать об этом на собственном опыте.


Предпосылка TDD vs DbC неверна

Эти две технологии не противоречат друг другу, а дополняют друг друга. Дополнение связано с размещением утверждений и целью.

Цель TDD включает как компоненты, так и область применения. Основными компонентами TDD являются логические утверждения и выполнение функции объекта (например, метода). Шаги просты:

  1. Создайте объект.
  2. Выполните некоторый код в функции.
  3. Сделайте утверждения о состоянии данных на объекте.

Утверждения, которые терпят неудачу, не проходят проверку. Цель - пройти все утверждения.

Как и TDD, контракты на разработку по контракту имеют цель, объем и компоненты. Хотя TDD ограничен временем модульного тестирования, контракты могут действовать в течение всего SDLC (жизненного цикла разработки программного обеспечения)! В рамках TDD выполнение методов (функций) объекта будет выполнять контракты. В настройке Eiffel Studio Auto-test (TDD) создается объект, выполняется вызов (точно так же, как TDD в других языках), но на этом сходство заканчивается.

В Eiffel Studio с автотестом и в коде Eiffel с контрактами цель несколько меняется. Мы хотим проверить отношения между клиентом и поставщиком. Наш код TDD притворяется клиентом нашего метода поставщика для своего объекта. Мы создаем наши объекты и вызываем методы, основанные на этой цели, а не просто на упрощенном «тестировании методов в стиле TDD». Поскольку вызовы наших методов (функций) имеют контракты, эти контракты будут выполняться как часть нашего TDD-кода в автоматическом тестировании. Поскольку это правда, утверждения (тесты) контракта, которые мы помещаем в наш код, НЕ должны появляться в нашем тестовом коде TDD. Наша задача (как программиста) просто обеспечить: A) код + контракты выполняются по всем N-путям, и B) код + контракты выполняются с использованием всех разумных типов данных и диапазонов.

Возможно, есть еще что написать об отношениях дополнения TDD-DbC, но я не буду с вами хамить по этому поводу. Достаточно сказать, что TDD и DbC НЕ расходятся с другими - далеко не во всех отношениях!

Сила контрактов DbC за пределами возможностей TDD

Теперь мы можем обратить наше внимание на силу контрактов Design-by-Contract за пределами возможностей TDD!

Контракты живут в коде. Они не внешние по отношению к нему, а внутренние. Самый важный момент (помимо договорных отношений между клиентом и поставщиком) в контрактах - это то, что компилятор спроектирован так, чтобы знать о них! Они НЕ являются неотъемлемым дополнением к Эйфелевой башне! Таким образом, они участвуют во всех аспектах наследования (как в традиционном вертикальном наследовании, так и в боковых или горизонтальных дженериках). Более того, они достигают места, недоступного для TDD - внутри метода (функции).

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

Более того, TDD не может создавать инварианты классов, кроме самых слабых способов. Я изо всех сил старался получить свой код автотеста (который на самом деле является просто версией прикладного TDD от Eiffel Studios) для имитации инвариантного класса. Это невозможно. Чтобы понять, почему вам нужно знать все детали того, как работают инварианты классов Эйфеля. Итак, на данный момент вам просто придется либо поверить мне на слово (или нет), что TDD неспособен к этой задаче, с которой DbC справляется так легко, хорошо и элегантно!

Возможности DbC не ограничиваются приведенными выше понятиями.

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

  1. Workbench: вы, как программист, используете код, чтобы увидеть, как он работает (например, до TDD-времени или в сочетании с TDD-временем).

  2. Модульное тестирование: ваше непрерывное интеграционное тестирование, модульное тестирование, TDD и т. Д.

  3. Альфа-тест: ваши первые тестовые пользователи будут спотыкаться о контрактах при запуске исполняемого файла.

  4. Бета-тест: более широкая аудитория пользователей также споткнется о контрактах.

  5. Производство: окончательный исполняемый файл (или производственная система) может подвергаться непрерывному тестированию посредством контрактов (TDD не может).

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

И если всего этого было недостаточно, контракты (по замыслу) могут делать то, что не может сделать ни одно утверждение TDD: сообщает вам, где в стеке вызовов и какие отношения клиент-поставщик разорваны и почему (что тоже сразу подсказывает, как это исправить). Почему это правда?

Утверждения TDD предназначены для того, чтобы сообщать вам о результатах выполнения кода постфактум. Утверждение TDD может видеть только текущее состояние исследуемого метода. Утверждения TDD не могут сказать вам, какой именно вызов завершился неудачно и почему! Видите ли, ваш первоначальный вызов TDD какого-либо метода вызовет этот метод. Часто этот метод вызывает другой, и еще один, и еще один - иногда, когда стек вызовов поднимается и опускается и то и дело, происходит сбой: что-то записывает данные неправильно, не записывает их вообще или записывает это когда это не должно.

TDD - это как полиция, появляющаяся на месте преступления после того, как убийство уже произошло. Все, что у них осталось, - это улики судебно-медицинской экспертизы, которые, как они надеются, приведут их к подозреваемому и обвинительному приговору. Но что, если бы мы могли присутствовать при совершении преступления? В этом разница между размещением утверждений TDD и утверждений контракта. Контракты предназначены для выявления совершаемого преступления, и они указывают прямо на преступника, совершающего преступление!

Резюме

Подведем итоги.

  • TDD не расходится с DbC.

  • Это дополняющий и кооперативный набор технологий, но с разными функциями и целями, а также инструменты для работы с ними.

  • Контракт расширяется и расскажет больше о вашем коде, когда он сломается.

  • TDD - это одна из форм катализатора исполнения контрактов.

В конце концов: я хочу и то, и другое! Прочитав все это (если вы выжили), надеюсь, вы тоже.

person Larry    schedule 23.02.2015
comment
После многих лет размышлений и переосмысления. Теперь я знаю, что это не TDD против DBC, а TDD + DBC - person Perpetualcoder; 23.02.2015

Разработка по контракту и разработка на основе тестирования не исключают друг друга.

В книге Бертрана Мейера Построение объектно-ориентированного программного обеспечения, 2-е издание не говорится, что вы никогда не допускаете ошибок. В самом деле, если вы посмотрите на главу «Когда контракт нарушен», в ней обсуждается, что происходит, когда функция не может выполнить то, что говорится в ее контракте.

Тот простой факт, что вы используете технику DbC, не делает ваш код правильным. Конструкция по контракту устанавливает четко определенные правила для вашего кода и его пользователей в форме контрактов. Это полезно, но вы всегда можете что-то напутать, только то, что вы, вероятно, заметите раньше.

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

person Daniel Daranas    schedule 26.01.2009

Я считаю, что лучше использовать оба метода вместе, а не один или другой.

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

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

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

person zdan    schedule 26.01.2009
comment
У вас может быть наивная, но медленная реализация вашей хеш-функции и гарантировать, что ваша оптимальная реализация вернет тот же ответ. Затем вы можете отключить этот конкретный контракт в производстве. - person marko.ristin; 21.01.2020

На мой взгляд, TDD более «индуктивный». Вы начинаете с примеров (тестовых случаев), и ваш код воплощает общее решение этих примеров.

DBC кажется более «дедуктивным»: после сбора требований вы определяете поведение объекта и контракты. Затем вы кодируете конкретную реализацию этих контрактов.

Написание контрактов несколько сложнее, чем тесты, которые являются конкретными примерами поведения, это может быть одной из причин, по которой TDD более популярен, чем DBC.

person Ryan    schedule 26.01.2009

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

person Jim Petkus    schedule 26.01.2009

Раньше я использовал оба варианта и нашел стиль DBC менее "навязчивым". Драйвером для DBC может быть обычное запущенное приложение. Для модульных тестов вы должны позаботиться о настройке, потому что вы ожидаете (проверяете) некоторые ответы. Для DBC этого не требуется. Правила написаны независимо от данных, поэтому нет необходимости настраивать и насмехаться над ними.

Подробнее о моем опыте работы с DBC / Python: http://blog.aplikacja.info/2012/04/classic-testing-vs-design-by-contract/

person Dariusz Cieslak    schedule 18.09.2012

Я рассматриваю проектирование по контракту как спецификацию успеха / неудачи во ВСЕХ случаях, тогда как разработка через тестирование нацелена на ОДИН конкретный случай. Если случай TDD завершается успешно, предполагается, что функция выполняет свою работу, но он не принимает во внимание другие случаи, которые могут привести к сбою.

С другой стороны, разработка по контракту не обязательно гарантирует желаемый ответ, только то, что ответ «правильный». Например, если функция, возвращаемая функцией, должна возвращать ненулевую строку, единственное, что вы можете предположить в ENSURE, - это то, что она не будет нулевой.

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

Итак, эти два понятия дополняют друг друга.

Грег

person Greg Gum    schedule 17.07.2013
comment
Если все, что у вас есть, - это молоток, которым можно работать как инструмент, то все начинает походить на гвоздь. Я пытался почитать о TDD, но чем больше я читаю, тем больше это похоже на истерию; Приятно читать что-то более уравновешенное. TDD - это инструмент, не более того. - person Avery Payne; 07.08.2014