Почему дизайн по контракту не так популярен по сравнению с разработкой на основе тестирования? - PullRequest
35 голосов
/ 26 января 2009

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

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

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

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

TDD

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

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

DBC

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

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


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

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

Ответы [ 9 ]

29 голосов
/ 27 января 2009

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

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

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

23 голосов
/ 27 января 2009

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

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

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

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

14 голосов
/ 23 февраля 2015

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


Предпосылка TDD против DbC * ​​1007 * неверна

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

Назначение 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 может достичь!

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

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

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

Достижение DbC не заканчивается вышеуказанными понятиями

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

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

  2. Юнит-тест: ваше непрерывное интеграционное тестирование, юнит-тестирование, TDD и т. Д.

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

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

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

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

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

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

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

Резюме

Давайте подведем итоги.

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

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

  • Достигните договор и узнайте больше о вашем коде, когда он сломается.

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

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

12 голосов
/ 27 января 2009

Разработка по контракту и разработка на основе испытаний не являются взаимоисключающими .

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

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

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

7 голосов
/ 27 января 2009

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

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

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

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

5 голосов
/ 19 сентября 2012

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

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

5 голосов
/ 27 января 2009

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

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

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

4 голосов
/ 17 июля 2013

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

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

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

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

Грег

4 голосов
/ 27 января 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...