Как согласовать TDD с контрактами интерфейса SUT? - PullRequest
3 голосов
/ 31 августа 2010

Предполагая, что мы реализуем с использованием TDD класс Stack, нам потребуется для каждого бита функциональности нашего класса Stack добавить новый тест, который выполняет его:

[TestMethod] public void Should_Be_Empty_After_Instantiation()
[TestMethod] public void Should_Not_Be_Empty_After_Pushing_One_Item()
...

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

Мой вопрос заключается в том, как совместить эти два аспекта.

Например, предполагая, что мой Stack использует внутри себя массив с начальным размером 8, я хочу, чтобы он увеличивался, если мой пользователь хочет вставить 9-й элемент. Для добавления этой функции изменения размера я хочу иметь по крайней мере один тест, который управляет моим кодом класса в этом направлении (я прав?).

С другой стороны, это будет добавление юнит-теста (или это действительно юнит-тест?), Который не соответствует действующему контракту класса (я предполагаю, что пользователь не заботится о внутренняя реализация стека), но его реализация.

Итак, у нас есть поворот, который я не знаю, как решить. Я здесь путаю понятия?

Спасибо

Редактировать

После долгих поисков я пришел по следующей ссылке, которая, похоже, решает эту проблему: http://stephenwalther.com/blog/archive/2009/04/11/tdd-tests-are-not-unit-tests.aspx

Ответы [ 3 ]

4 голосов
/ 31 августа 2010

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

Теперь написание тестов TDD часто сообщает автору о возможных пробелах в его API. В этом случае тест хотел бы иметь возможность указать начальный размер предварительного стека. Затем он может установить его на 8 или 2 или что-то еще и нажать еще один элемент, чем этот. И нередко думать, что другие клиенты тоже могут этого захотеть (например, это похоже на резервный метод std :: vector). Итак, я бы подумал о добавлении параметра конструктора в стек, чтобы указать начальный зарезервированный размер, по умолчанию он равен 8 и добавлен тест Should_Not_Error_When_Pushing_More_Items_Than_Initial_Size.

1 голос
/ 31 августа 2010

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

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

0 голосов
/ 07 сентября 2010

У вас здесь есть пара аспектов поведения:

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

Если у вас возникают проблемы при тестировании их обоих, вы всегда можете поместить поведение, которое превращает внутренний массив в другой класс, отделив его от поведения стека. Это принцип единой ответственности в перегрузке! Затем вы можете просто использовать очень большой массив для тестирования поведения вашего стека и проверить, что стек правильно делегирует свои обязанности, когда речь идет о росте, используя макеты (вы можете свернуть свои собственные, если у вас нет фреймворка для насмешек).

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

...