Здравствуйте, zfima, Вы писали:
Z>>>Или TDD подразумевает Unit Test'ы исключительно для имплементаций интерфейсов? Q>>Как только перестанешь называть свои тесты unit test'ами, это сразу же упростит жизнь. Z>Пожалуйста, можешь пояснить?
Что тут пояснять — просто берёшь и тестируешь в своё удовольствие. Тесты не должны быть хрупкими, по возможности должны быть изолированными, ну и прочие базовые рекомендации. Даже если им более или менее следовать, всё равно найдутся теоретики, которые скажут, что твои юнит-тесты написаны не по феншую, завещанному Мартином Фаулером, Кентом Беком или какие там есть сектанты от TDD. На что ты отвечаешь, что твои тесты никогда и не претендовали на звание «юнит-», и не занимаешься философией на тему, что там подразумевает TDD.
Здравствуйте, Sinix, Вы писали:
S>Всё гораздо сложнее Перечитай ветку — начальное утверждение не моё, тезисов я никаких не высказывал и всё моё участие свелось к примерам когда цитата выше неверна.
Всё горазжо проще, вы просто выпали из контекста.
Фразу "значит, он работает правильно" следует читать как "значит, что для сценариев, которые покрываются тестами при данных конкретных входных параметрах при условии работы на указаном програмно-аппаратном компелексе бла-бла-бла работает правильно".
Re: TDD в solution'е, в котором не может быть Test Project'а
Здравствуйте, zfima, Вы писали:
Z>Здравствуйте
Z>У меня есть здоровый проект на C#. Z>Solution, в котором 3 проекта. Z>Solution не предполагает, что проект, типа Test Project, будет включен в него.
Z>Есть возможность тестировать dll'и, конечно. Но что тогда с разными функциями внутри классов, которые приватные? Или TDD подразумевает Unit Test'ы исключительно для имплементаций интерфейсов?
Z>Спасибо
Здравствуйте, Yoriсk, Вы писали:
Y>Если с ними что-то не так, то завалится тест метода публичного интерфейса, который этот приватный метод вызывает и ошибка, в общем, найдена.
Между «работает» и «завалится» может быть спектр оттенков серого.
Y>и ошибка, в общем, найдена.
Чтобы это было так просто, тесты должны быть изолированными. Как правило, проверять за раз одну единицу функционала, реже склейку двух единиц, потом интеграцию нескольких компонентов, и так далее. Если проверять сразу весь пайплайн и только его, то большая часть сценариев развития событий останется непокрытой, а в покрытой будет сложно диагностировать ошибку.
Y>А если он не падает при исспользовании его классом то, значит, он работает правильно.
Не значит.
Публичный API может быть совсем высокоуровневым, содержать всего пару-тройку функций типа «сделай мне красиво». Логика же может содержать нетривиальные алгоритмы и структуры данных, множество разных путей исполнения. Покрытие этих путей слишком сложно гарантировать, если их выбор определяется косвенно и ненадёжно (неустойчиво) через высокоуровневые входные параметры пайплайна-в-целом.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: TDD в solution'е, в котором не может быть Test Project'а
Здравствуйте, zfima, Вы писали:
Z>>Есть возможность тестировать dll'и, конечно. Но что тогда с разными функциями внутри классов, которые приватные? Или TDD подразумевает Unit Test'ы исключительно для имплементаций интерфейсов? Z>>Спасибо Z>В принципе, я согласен с Опять про тестирование приватных функций
Да, совершенно верно. А всякие хаки с InternalsVisibleToAttribute — фтопку.
И по моему опыту получается что "принципиально нетестируемый код" (с) всегда оказывается обычным г которое необходимо рефакторить.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
TDD в solution'е, в котором не может быть Test Project'а
У меня есть здоровый проект на C#.
Solution, в котором 3 проекта.
Solution не предполагает, что проект, типа Test Project, будет включен в него.
Есть возможность тестировать dll'и, конечно. Но что тогда с разными функциями внутри классов, которые приватные? Или TDD подразумевает Unit Test'ы исключительно для имплементаций интерфейсов?
Здравствуйте, zfima, Вы писали:
Z>У меня есть здоровый проект на C#. Z>Solution, в котором 3 проекта. Z>Solution не предполагает, что проект, типа Test Project, будет включен в него.
Ну заведи себе рядом второй файл солюшена AnotherSolutionWithTests.sln, с другим набором ссылок на проекты (подмножество твоих трёх проектов плюс тестовые проекты).
Z>Или TDD подразумевает Unit Test'ы исключительно для имплементаций интерфейсов?
Как только перестанешь называть свои тесты unit test'ами, это сразу же упростит жизнь.
Здравствуйте, Qbit86, Вы писали:
Z>>Или TDD подразумевает Unit Test'ы исключительно для имплементаций интерфейсов?
Q>Как только перестанешь называть свои тесты unit test'ами, это сразу же упростит жизнь.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, zfima, Вы писали:
Z>>>>Или TDD подразумевает Unit Test'ы исключительно для имплементаций интерфейсов? Q>>>Как только перестанешь называть свои тесты unit test'ами, это сразу же упростит жизнь. Z>>Пожалуйста, можешь пояснить?
Q>Что тут пояснять — просто берёшь и тестируешь в своё удовольствие. Тесты не должны быть хрупкими, по возможности должны быть изолированными, ну и прочие базовые рекомендации. Даже если им более или менее следовать, всё равно найдутся теоретики, которые скажут, что твои юнит-тесты написаны не по феншую, завещанному Мартином Фаулером, Кентом Беком или какие там есть сектанты от TDD. На что ты отвечаешь, что твои тесты никогда и не претендовали на звание «юнит-», и не занимаешься философией на тему, что там подразумевает TDD.
Спасибо, понятно.
Ну а как с тестированием функций, которые не являются имплементацией интерфейса? Или мне это должно быть "прозрачно"?
Хотя, честно говоря, мне это совсем не прозрачно. Как же тогда протестировать их?
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, zfima, Вы писали:
Z>>Ну а как с тестированием функций, которые не являются имплементацией интерфейса?
Q>В .NET есть атрибут InternalsVisibleToAttribute, который явно разрешает указанным в нём сборкам обращаться к внутренностям.
Здравствуйте, zfima, Вы писали:
Z>Ну а как с тестированием функций, которые не являются имплементацией интерфейса? Или мне это должно быть "прозрачно"? Z>Хотя, честно говоря, мне это совсем не прозрачно. Как же тогда протестировать их?
Например никак. Вообще. Если с ними что-то не так, то завалится тест метода публичного интерфейса, который этот приватный метод вызывает и ошибка, в общем, найдена. А если он не падает при исспользовании его классом то, значит, он работает правильно.
Да, это не по феншую, зато не нужны пляски с бубном вокруг "совсем-совсем приватных, но всё таки чуть-чуть доступных извне" ну и в конце концов — вам ехать или шашечки соответствия "чистому" TDD?
Здравствуйте, Qbit86, Вы писали:
Y>>А если он не падает при исспользовании его классом то, значит, он работает правильно. Q>Не значит.
А можно пример?
Q>Публичный API может быть совсем высокоуровневым, содержать всего пару-тройку функций типа «сделай мне красиво». Логика же может содержать нетривиальные алгоритмы и структуры данных, множество разных путей исполнения. Покрытие этих путей слишком сложно гарантировать, если их выбор определяется косвенно и ненадёжно (неустойчиво) через высокоуровневые входные параметры пайплайна-в-целом.
Публичный API != модуль (unit). Если именно такой громадный модуль, значит вы делаете что-то не то. Обычно один класс == один модуль. И неприватные методы класса запросто тестируются (если конечно SRP, ISP, DIP и прочие красивые аббревиатуры).
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ., Вы писали:
.>А можно пример?
Легко — утечка ресурсов, проглатывание исключений без логирования, ошибки с часовыми поясами/культурой/кодировкой etc. Такие вещи крайне плохо ловятся тестами.
.>Обычно один класс == один модуль. И неприватные методы класса запросто тестируются (если конечно SRP, ISP, DIP и прочие красивые аббревиатуры).
Далеко не всегда. В реальных бизнес-сценариях ценность имеют не отдельные методы, а то, что из них собрали. Даже при условии отсутствия ошибок в каждой отдельной части — не факт что целое будет работать. Применение волшебных букв тут не спасает, только ассерты (контракты) и тесты по сценариям использования.
Здравствуйте, Sinix, Вы писали:
.>>А можно пример? S>Легко — утечка ресурсов, проглатывание исключений без логирования, ошибки с часовыми поясами/культурой/кодировкой etc. Такие вещи крайне плохо ловятся тестами.
Утечка ресурсов? А что такого сложного, проверить, что close() был вызван? Глобальные переменные-коллекции же не используем?
Исключения? Обычно такое легко IDE и прочие checkstyle ловит. Да и вообще, если у тебя catch-блок, то чтобы его покрыть, ты должен написать юнит-тест. Если стоит довольно высокий порог code coverage, то ловится.
Часовые пояса-культуры? Пиши юнит тесты для экзотических культур, часовой пояс Монголии, корейская локаль, японский календарь, кодировка только UTF-8 — и точно что-нибудь, да навернётся. Сделай CI-сервер, чтобы он запускал тесткейсы в нестандартных локалях, неожиданных для разработчика.
Давай посложнее пример.
.>>Обычно один класс == один модуль. И неприватные методы класса запросто тестируются (если конечно SRP, ISP, DIP и прочие красивые аббревиатуры). S>Далеко не всегда. В реальных бизнес-сценариях ценность имеют не отдельные методы, а то, что из них собрали. Даже при условии отсутствия ошибок в каждой отдельной части — не факт что целое будет работать. Применение волшебных букв тут не спасает, только ассерты (контракты) и тесты по сценариям использования.
Есть же интеграционные тесты, функциональные, к которым уже несколько другие требования.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ., Вы писали:
.>>>А можно пример? S>>Легко — утечка ресурсов, проглатывание исключений без логирования, ошибки с часовыми поясами/культурой/кодировкой etc. Такие вещи крайне плохо ловятся тестами.
Я не понимаю, о чём мы спорим
Смотри сам:
Y>>>А если он (тест публичного API — Sinix) не падает при исспользовании его классом то, значит, он работает правильно.
Q>>Не значит.
.>А можно пример?
... примеры поскипаны. Такие вещи крайне плохо ловятся тестами.
Твои доводы:
.>Исключения? Обычно такое легко IDE и прочие checkstyle ловит. Да и вообще, если у тебя catch-блок, то чтобы его покрыть, ты должен написать юнит-тест.
.>Часовые пояса-культуры? Пиши юнит тесты для экзотических культур
.>Есть же интеграционные тесты, функциональные, к которым уже несколько другие требования.
Если это не "крайне плохо ловятся тестами", то что?
В реальном бизнес-коде польза от тотального покрытия ещё меньше — самые гадкие ошибки возникают при дичайшем стечении обстоятельств, которое в тестах воспроизвести нереально.
Из практики — надёжно работают только ассерты, да и то не всегда. Самый простой пример:
static double Sample(double a, double b, double c)
{
if (a > 1) a = 1;
if (b <= 1) b = 1;
Code.AssertState(a != b, "Some detailed message");
return c / (a - b);
}
a, b и c считаются в независимых частях кода, каждая часть — примерно 50-100 строк оттестированного кода с подборкой коэффициентов из кучи шкал. Все мыслимые тесты зелёные, интеграционные — тоже (a != b). Благодаря ассерту всё-таки нашлась ошибка, но уже у клиента.
Здравствуйте, Sinix, Вы писали:
S>Если это не "крайне плохо ловятся тестами", то что?
Так они "крайне плохо ловятся тестами" что при тестировании через публичный интерфейс что при хаках с SetThisPrivateFunctionAsPublic. В чём тогда профит?
Здравствуйте, Yoriсk, Вы писали:
S>>Если это не "крайне плохо ловятся тестами", то что? Y>Так они "крайне плохо ловятся тестами" что при тестировании через публичный интерфейс что при хаках с SetThisPrivateFunctionAsPublic. В чём тогда профит?
А кто говорил про профит? Начальное утверждение было такое:
Если с ними (с приватными методами — Sinix) что-то не так, то завалится тест метода публичного интерфейса, который этот приватный метод вызывает и ошибка, в общем, найдена. А если он не падает при исспользовании его классом то, значит, он работает правильно.
Примеры когда это утверждение неверно, я привёл, за профитом — это наверно к ув. .
Здравствуйте, Sinix, Вы писали:
S>>>Если это не "крайне плохо ловятся тестами", то что? Y>>Так они "крайне плохо ловятся тестами" что при тестировании через публичный интерфейс что при хаках с SetThisPrivateFunctionAsPublic. В чём тогда профит?
S>А кто говорил про профит? Начальное утверждение было такое: S>
S>Если с ними (с приватными методами — Sinix) что-то не так, то завалится тест метода публичного интерфейса, который этот приватный метод вызывает и ошибка, в общем, найдена. А если он не падает при исспользовании его классом то, значит, он работает правильно.
S>Примеры когда это утверждение неверно, я привёл, за профитом — это наверно к ув. .
Ты просто высказал этот тезис в топике обсуждения тестирования приватных методов.
Я подумал, что этот тезис высказывается в пользу тестирования приватных методов. Видимо это ты сказал так, не в тему, просто чтобы высказать наболевшее, а не защищая высказанные тезисы. Вот и получилось непонимание, ибо я предположил, что идёт обсуждение топика, а не просто трёп.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ., Вы писали:
S>А кто говорил про профит? Начальное утверждение было такое:
Если с ними (с приватными методами — Sinix) что-то не так, то завалится тест метода публичного интерфейса, который этот приватный метод вызывает и ошибка, в общем, найдена. А если он не падает при исспользовании его классом то, значит, он работает правильно.
S>>Примеры когда это утверждение неверно, я привёл, за профитом — это наверно к ув. . .>Ты просто высказал этот тезис в топике обсуждения тестирования приватных методов.
.>Я подумал, что этот тезис высказывается в пользу тестирования приватных методов. Видимо это ты сказал так, не в тему, просто чтобы высказать наболевшее, а не защищая высказанные тезисы. Вот и получилось непонимание, ибо я предположил, что идёт обсуждение топика, а не просто трёп.
Всё гораздо сложнее Перечитай ветку — начальное утверждение не моё, тезисов я никаких не высказывал и всё моё участие свелось к примерам когда цитата выше неверна.
Здравствуйте, Sinix, Вы писали:
S>В реальном бизнес-коде польза от тотального покрытия ещё меньше — самые гадкие ошибки возникают при дичайшем стечении обстоятельств, которое в тестах воспроизвести нереально. S>Из практики — надёжно работают только ассерты, да и то не всегда. Самый простой пример: S>
S> static double Sample(double a, double b, double c)
S> {
S> if (a > 1) a = 1;
S> if (b <= 1) b = 1;
S> Code.AssertState(a != b, "Some detailed message");
S> return c / (a - b);
S> }
S>
S>a, b и c считаются в независимых частях кода, каждая часть — примерно 50-100 строк оттестированного кода с подборкой коэффициентов из кучи шкал. Все мыслимые тесты зелёные, интеграционные — тоже (a != b). Благодаря ассерту всё-таки нашлась ошибка, но уже у клиента.
Что-то это меня тоже не убеждает, скорее наоборот, подтверждает значительность первой буковки D в аббревиатуре TDD. Если пишешь X / Y нужно задуматься, а что будет если Y==0... это один из классических крайних случаев, первый кандидат в юнит-тесты. Притом, раз уж ты догадался ассерт вставить, то лучше вместо ассерта уж лучше придумать такой юнит-тест, который воспроизведёт ситуацию. И в этом юнит-тесте будет сразу видно какая конкретная бизнес-ситуация соответствует этому странному стечению обстоятельств, что тоже поможет найти ошибку, но не у клиента, а на этапе написания кода, ещё до публикации коммитов.
А если получается так, что сложно воспроизвести эту ситуацию из-за кучи шкал и прочих "независимых" (хе-хе) частей кода, значит код говно и надо рефакторить этот юнит-переросток, разбивая его на проще тестируемые мелкие юниты.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай