Модульное тестирование - особенно в C ++ - требует от тестируемого кода добавить больше швов, которые будут строго требоваться для данной проблемы.
Только если вы не считаете тестирование неотъемлемой частью решения проблем. Для любой нетривиальной проблемы это должно быть не только в мире программного обеспечения.
В мире аппаратного обеспечения это было изучено давно - трудным путем. Производители различного оборудования на протяжении веков извлекли уроки из бесчисленных падающих мостов, взрывающихся машин, курящих процессоров и т. Д. И т. Д., Чему мы сейчас учимся в мире программного обеспечения. Все они встраивают «дополнительные швы» в свои продукты, чтобы сделать их тестируемыми. Большинство новых автомобилей в настоящее время имеют диагностические порты для ремонтников, чтобы получить данные о том, что происходит внутри двигателя. Значительная часть транзисторов на каждом процессоре служит для диагностических целей. В мире аппаратного обеспечения каждый бит «лишних» вещей стоит, а когда продукт производится миллионами, эти затраты, безусловно, составляют большие суммы денег. Тем не менее, производители готовы потратить все эти деньги на тестируемость.
Возвращаясь к миру программного обеспечения, C ++ действительно сложнее для модульного тестирования, чем более поздние языки с динамической загрузкой классов, отражением и т. Д. Тем не менее, большинство проблем можно хотя бы уменьшить. В одном проекте C ++, где я до сих пор использовал модульные тесты, мы не запускали тесты так часто, как, например, в Java-проекте - но все же они были частью нашей сборки CI, и мы сочли их полезными.
Является ли способ, по которому модульные тесты требуют структурирования кода приложения, «только» выгодным для модульных тестов, или это действительно выгодно для структуры приложений?
По моему опыту, тестируемый дизайн полезен в целом, а не только для модульных тестов. Эти преимущества приходят на разных уровнях:
- Тестирование вашего проекта заставляет вас разбивать ваше приложение на небольшие, более или менее независимые части, которые могут влиять друг на друга только ограниченным и четко определенным образом - это очень важно для долгосрочной стабильности и ремонтопригодности вашей программы. Без этого код имеет тенденцию ухудшаться до спагетти-кода, где любое изменение, сделанное в любой части кодовой базы, может вызвать неожиданные эффекты в, казалось бы, не связанных, отдельных частях программы. Что, разумеется, является кошмаром для каждого программиста.
- Написание самих тестов в стиле TDD фактически проверяет ваши API, классы и методы и служит очень эффективным тестом для определения того, имеет ли смысл ваш дизайн - если написание тестов с интерфейсом кажется неудобным или трудным, вы получаете ценную раннюю обратную связь, когда она все еще легко сформировать API. Другими словами, это защищает вас от преждевременной публикации ваших API.
- Модель разработки, внедренная TDD, помогает вам сосредоточиться на конкретной задаче (задачах) и удерживает вас на цели, сводя к минимуму шансы на то, что вы заблудитесь в решении других проблем, помимо той, которую вы должны, добавив ненужные дополнительные функции и сложность. , и т.д.
- Быстрая обратная связь модульных тестов позволяет вам быть смелым в рефакторинге кода, позволяя вам постоянно адаптировать и развивать дизайн в течение всего жизненного цикла кода, таким образом эффективно предотвращая энтропию кода.
Я помню эмпирическое правило, которое гласило: не обобщайте, пока вам не понадобится / пока не появится второе место, которое использует код. С юнит-тестами всегда есть второе место, где используется код, а именно юнит-тест. Так достаточно ли этой причины для обобщения?
Если вы можете доказать, что ваше программное обеспечение выполняет именно то, для чего оно предназначено, и доказать это быстрым, повторяемым, дешевым и достаточно детерминированным способом, чтобы удовлетворить ваших клиентов - без «лишних» обобщений или швов, вызванных юнит-тестами, сделайте это. (и дайте нам знать, как вы это делаете, потому что я уверен, что многие люди на этом форуме будут заинтересованы так же, как и я :-)
Между прочим, я предполагаю, что под «обобщением» вы подразумеваете такие вещи, как введение интерфейса (абстрактного класса) и полиморфизма вместо одного конкретного класса - если нет, уточните, пожалуйста.