...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
01.10.11 16:19: Перенесено модератором из 'О жизни' — AndrewVK
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Самая базовая защита от примитивных багов и опечаток.
Я сам не писал до сих пор тесты. Но в ходе работы были мысли о том, когда они могут сыграть роль.
Например, ты разрабатываешь большой класс, с большим функционалом и что самое главное взаимосвязями.
Через некоторое время становится тяжело держать в голове все эти взаимосвязи и соответственно ты не осознаешь в каком месте новая фича вызовет некорректную работу предыдущего кода. В это случае ты вызываешь свой набор(их может быть много) тестов и проверяешь не уронили ли твои новые две строчки кода твой класс в месте, который ты написал месяц назад (и потестил). При условии отсутствия тестов, тебе на каждые новые две строчки кода придется заново все тестировать вручную, что в некоторых случаях проблематично или очень проблематично или невозможно.
Если ты пишешь кнопочку, то тестировать ее сильно не надо, а вот если пишешь софт для самолета, то тут цена ошибки сам понимаешь высока. И возможно набор тестов сможет уберечь от ошибки. Гарантии 100 процентов конечно нет.
Здравствуйте, HolyNick, Вы писали:
HN>Я сам не писал до сих пор тесты. Но в ходе работы были мысли о том, когда они могут сыграть роль. HN>Например, ты разрабатываешь большой класс, с большим функционалом и что самое главное взаимосвязями.
У меня такие же мысли. Если логика кода очень сложная и путаная, тогда и только тогда тесты могут пригодиться.
А насчет большого класса, не пахнет ли тут антипаттерном, а именно "God object", то есть, один огроменный класс, который делает чуть ли не все вообще? Так лучше его распилить на самостоятельные части и работать станет проще.
HN>Если ты пишешь кнопочку, то тестировать ее сильно не надо, а вот если пишешь софт для самолета, то тут цена ошибки сам понимаешь высока. И возможно набор тестов сможет уберечь от ошибки. Гарантии 100 процентов конечно нет.
DD>А насчет большого класса, не пахнет ли тут антипаттерном, а именно "God object", то есть, один огроменный класс, который делает чуть ли не все вообще? Так лучше его распилить на самостоятельные части и работать станет проще.
Архитектура отдельная песня. Но ключевая проблема — большое число тяжело отслеживаемых связей (которые могут присутствовать даже при хорошей архитектуре).
PS: Просто при плохой архитектуре вообще обалдеешь вносить(безошибочно) какие-то изменения.
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
А кто сказал, что они на практике ничего не ловят? У нас периодически что-то ловят.
А для описанных тобой проблем есть автоматические интеграционные тесты, которые у нас тоже ловят и ещё как!
Здравствуйте, HolyNick, Вы писали:
HN>Если ты пишешь кнопочку, то тестировать ее сильно не надо, а вот если пишешь софт для самолета, то тут цена ошибки сам понимаешь высока.
Насколько я знаю, жизненно критический код пишется (ну или должен писаться) совсем по другому, подход к написанию более формальный и математический. Тестами все случаи покрыть в большинстве случаев в принципе невозможно.
Здравствуйте, RiNSpy, Вы писали:
RNS>Насколько я знаю, жизненно критический код пишется (ну или должен писаться) совсем по другому, подход к написанию более формальный и математический.
Что Вы имеете ввиду под "более формальными и математическими подходами"? Насколько они заменяют\отменяют тестирование?
Здравствуйте, HolyNick, Вы писали:
HN>Здравствуйте, RiNSpy, Вы писали:
RNS>>Насколько я знаю, жизненно критический код пишется (ну или должен писаться) совсем по другому, подход к написанию более формальный и математический.
HN>Что Вы имеете ввиду под "более формальными и математическими подходами"? Насколько они заменяют\отменяют тестирование?
Теоретически — заменяют и отменяют полностью.
На практике, конечно, тесты всё равно нужны — хотя бы чтобы проверить, что оборудование и среда продолжают соответствовать тем теоретическим условиям, из которых производилась формальная верификация. Но если предположить, что они соответствуют, то тесты излишни.
Здравствуйте, DorfDepp, Вы писали:
DD>У меня такие же мысли. Если логика кода очень сложная и путаная, тогда и только тогда тесты могут пригодиться.
И главное тут — определить смысл слова "очень". Как у Остёра: 4 — это куча или ещё не куча?
Вот, например, это "очень" или нет?
get_evts(All) ->
%%% SELECT category, level, MIN(timestamp) FROM events \
%%% GROUP BY category,level
%%% NB: input is already sorted literally
lists:reverse(rec_get_evts([], All)).
rec_get_evts(Out, []) ->
Out;
rec_get_evts([{C,L,T1}|ORest], [{C,L,_,T2}|IRest]) when T1 > T2 ->
rec_get_evts([{C,L,T2}|ORest], IRest);
rec_get_evts([{C,L,T1}|ORest], [{C,L,_,_T2}|IRest]) ->
rec_get_evts([{C,L,T1}|ORest], IRest);
rec_get_evts(Out, [{C,L,_,T}|IRest]) ->
rec_get_evts([{C,L,T}|Out], IRest).
Я нарисовал на него тесты, начиная с самых простых (на входе пустой список) и до путаных входных ситуаций, и я спокоен.
DD>А насчет большого класса, не пахнет ли тут антипаттерном, а именно "God object", то есть, один огроменный класс, который делает чуть ли не все вообще? Так лучше его распилить на самостоятельные части и работать станет проще.
IMHO, всегда найдётся что-то, в чём не уверен, даже в достаточно простом классе.
А тесты — они внешняя для разработчика гарантия (хотя бы частичная), что он не ошибся.
Кстати, при чём тут класс? Вот у меня тут вообще классов как таковых не бывает (хотя есть некоторые приближения к тому, что называют этим словом в Simula/SmallTalk/etc.)
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Рассмотри проблему с обратного конца.
Прежде, чем писать текст метода, напиши, как ты будешь этот метод использовать.
Это и будут unit-тесты.
Это — не тестирование. Это разработка (через тестирование).
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, LaptevVV, Вы писали:
LVV>Рассмотри проблему с обратного конца. LVV>Прежде, чем писать текст метода, напиши, как ты будешь этот метод использовать. LVV>Это и будут unit-тесты. LVV>Это — не тестирование. Это разработка (через тестирование).
Часто один метод юзается в 1-2 местах. Это никак нельзя назвать юнит-тестированием. Ты, кстати, сам пробовал так делать?
Здравствуйте, lozzy, Вы писали:
L>Здравствуйте, LaptevVV, Вы писали:
LVV>>Рассмотри проблему с обратного конца. LVV>>Прежде, чем писать текст метода, напиши, как ты будешь этот метод использовать. LVV>>Это и будут unit-тесты. LVV>>Это — не тестирование. Это разработка (через тестирование).
L>Часто один метод юзается в 1-2 местах. Это никак нельзя назвать юнит-тестированием. Ты, кстати, сам пробовал так делать?
Да. Реально помогает нюансы выяснить.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, LaptevVV, Вы писали:
LVV>Рассмотри проблему с обратного конца. LVV>Прежде, чем писать текст метода, напиши, как ты будешь этот метод использовать. LVV>Это и будут unit-тесты. LVV>Это — не тестирование. Это разработка (через тестирование).
Во-первых, TDD и юнит-тесты, это вовсе не одно и то же.
Во-вторых, в TDD скорее функциональные тесты основную рояль играют, а не юнит.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, Mystic, Вы писали:
M>Здравствуйте, DorfDepp, Вы писали:
DD>>У меня такие же мысли. Если логика кода очень сложная и путаная, тогда и только тогда тесты могут пригодиться.
M>Если логика работы сложная и запутанная, то обычно частями ее проверить сложно, только все целиком и полностью. А это как-бы уже не unit-тест. :)
Вот в этой книге:
хорошо и подробно объясняется, как превратить такой код в легко проверяемый, вплоть до того, что всё покрывается юнит-тестами. А если вы этого не делаете, то таки да, легко не будет.
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Говорят, они хороши, когда уже сильно потом класс ковыряешь. Подковырнул, тесты-то и посыпались, сидишь, чешешь репу
Только эта полезность произойдет лишь если интерфейс класса остается обратно совместимым. А иначе придется еще и тесты ковырять, не только класс.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, LaptevVV, Вы писали:
LVV>>Рассмотри проблему с обратного конца. LVV>>Прежде, чем писать текст метода, напиши, как ты будешь этот метод использовать. LVV>>Это и будут unit-тесты. LVV>>Это — не тестирование. Это разработка (через тестирование).
AVK>Во-первых, TDD и юнит-тесты, это вовсе не одно и то же. AVK>Во-вторых, в TDD скорее функциональные тесты основную рояль играют, а не юнит.
Это скорее справедливо для behavior-driven design (BDD), там основные тесты — именно функциональные на целевые действия. А TDD такого ограничения не предполагает, скорее наоборот — в полном виде оно требует такого контроля на мельчайшие детали разработки.
(Да, я понимаю, что у каждого свой источник знаний. Но я массово видел именно такое различие)
Здравствуйте, netch80, Вы писали:
N>хорошо и подробно объясняется, как превратить такой код в легко проверяемый, вплоть до того, что всё покрывается юнит-тестами. А если вы этого не делаете, то таки да, легко не будет.
Не скажу, что сейчас все трудно. Но часто ошибки в том, что алгоритм (улучшение) просто не решает поставленную задачу, хотя он реализован без ошибок в точности, как было задумано. Потому что находится некий неучтенный набор входных данных.
Здравствуйте, Mystic, Вы писали:
M>Здравствуйте, netch80, Вы писали:
N>>хорошо и подробно объясняется, как превратить такой код в легко проверяемый, вплоть до того, что всё покрывается юнит-тестами. А если вы этого не делаете, то таки да, легко не будет.
M>Не скажу, что сейчас все трудно. Но часто ошибки в том, что алгоритм (улучшение) просто не решает поставленную задачу, хотя он реализован без ошибок в точности, как было задумано. Потому что находится некий неучтенный набор входных данных.
И такое бывает, сплошь и рядом. Но это не значит, что тестировать не надо или что не надо стараться делать систему пригодной к тестированию.:)
Здравствуйте, netch80, Вы писали:
AVK>>Во-вторых, в TDD скорее функциональные тесты основную рояль играют, а не юнит.
N>Это скорее справедливо для behavior-driven design (BDD)
Согласно википедии:
It extends TDD by writing test cases in a natural language that non-programmers can read. Behavior-driven developers use their native language in combination with the ubiquitous language of domain driven design to describe the purpose and benefit of their code. This allows the developers to focus on why the code should be created, rather than the technical details, and minimizes translation between the technical language in which the code is written and the domain language spoken by the business, users, stakeholders, project management, etc.
Так что вряд ли. BDD, получается, ближе к классическому водопадному подходу, где юзкейсы описываются на естественном языке или UML.
N>, там основные тесты — именно функциональные на целевые действия. А TDD такого ограничения не предполагает, скорее наоборот — в полном виде оно требует такого контроля на мельчайшие детали разработки.
Согласно википедии:
Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards.
Так что TDD все таки функциональные тесты предполагает в обязательном порядке. А unit уже по желанию. Что, помимо прочего, еще и логично, так как основная идея TDD состоит в описании ТЗ (то бишь функционала) в виде тестов, что как раз таки функциональные тесты и обеспечивают, а никак не unit, которые зависят от конкретного, уже готового дизайна приложения. Скорее всего, попутал ты TDD с XP. Вот последнее как раз таки именно на unit-тестирование ориентировано.
N>(Да, я понимаю, что у каждого свой источник знаний. Но я массово видел именно такое различие)
Википедия — достаточно массово?
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, Pzz, Вы писали:
Pzz>Говорят, они хороши, когда уже сильно потом класс ковыряешь. Подковырнул, тесты-то и посыпались, сидишь, чешешь репу
Когда сильно ковыряешь, unit-тесты очень плохи, так как висят свинцовой гирей и мешают проводить глубокий рефакторинг. А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне.
Pzz>А иначе придется еще и тесты ковырять, не только класс.
Вот именно.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Может я неправильно все делаю, но именно юнит-тестами удобно отлаживать сколь-нибудь сложную логику.
Ну а потом они просто остаются. Есть пить почти не просят.
Здравствуйте, HolyNick, Вы писали:
HN>Я сам не писал до сих пор тесты. Но в ходе работы были мысли о том, когда они могут сыграть роль. HN>Например, ты разрабатываешь большой класс, с большим функционалом и что самое главное взаимосвязями. HN>Через некоторое время становится тяжело держать в голове все эти взаимосвязи и соответственно ты не осознаешь в каком месте новая фича вызовет некорректную работу предыдущего кода. В это случае ты вызываешь свой набор(их может быть много) тестов и проверяешь не уронили ли твои новые две строчки кода твой класс в месте, который ты написал месяц назад (и потестил). При условии отсутствия тестов, тебе на каждые новые две строчки кода придется заново все тестировать вручную, что в некоторых случаях проблематично или очень проблематично или невозможно. HN>Если ты пишешь кнопочку, то тестировать ее сильно не надо, а вот если пишешь софт для самолета, то тут цена ошибки сам понимаешь высока. И возможно набор тестов сможет уберечь от ошибки. Гарантии 100 процентов конечно нет.
Тесты большей частью нужны не для того, чтобы проверять не упал ли твой код где-то при рефакторинге, а чтобы сделать хороший дизайн приложения. С тестами как раз будет слабосвязанный код, чистый и понятный, иначе тесты не напишутся. Например, нужно написать алгоритм обработки чего-то, то вместе того, чтобы внутри этого класса ложить помимо логики работу с бд, сетью, валидацию, использование других компонентов/сервисов, ты делаешь небольшой класс, который делает что-то конкретное, и за другим функционалом лезет в другие классы, а кто там — реальный ли класс или мок — это уже не его забота. Тут вдруг неожиданно сам по себе реализуется SRP и прочий SOLID, и вообще ООП, т.к. класс делает только то, что должен делать, и ничего больше.
Попробуйте покрыть тестами свой проект, много интересного о его дизайне поймете
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Сколько заблуждений в таком коротком тексте...
1)Unit-тесты ловят очень хорошо баги регрессии. Когда в следствии рефакторинга или других манипуляций с кодом меняется его поведение, а не должно
2)Unit-тесты абсолютно нет смысла писать после кода, после кода стоит писать интеграционные тесты, которые как раз будут взаимодействие компонент проверять
3)Польза от unit-тестов огромная, так как правильно написанные тесты помогают проверять корректность кода очень быстро не выходя из из среды разработки.
4)Но unit-тесты — тоже код, который надо поддерживать и меня в случае изменений требований, это дает оверхед, иногда весьма значительный.
Если вы создавая приложение выделяете некоторую стабильную часть кода, которую редко затрагивают при изменении требований, и которая используется другими частями приложения, то её нужно покрывать unit-тестами.
Если ваш код сильно нетривиален и по интерфейсу реализация совсем не очевидна, то также unit-тесты вам помогут.
Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты.
AVK>Так что TDD все таки функциональные тесты предполагает в обязательном порядке. А unit уже по желанию. Что, помимо прочего, еще и логично, так как основная идея TDD состоит в описании ТЗ (то бишь функционала) в виде тестов, что как раз таки функциональные тесты и обеспечивают, а никак не unit, которые зависят от конкретного, уже готового дизайна приложения. Скорее всего, попутал ты TDD с XP. Вот последнее как раз таки именно на unit-тестирование ориентировано.
В TDD/BDD используется несколько уровней тестов и пишутся они outside-in — от end-to-end/acceptance через integration к unit. Пишем красный end-to-end/acceptance тест, описывающий (тестирующий) фичу (cucumber, fitnesse и т.д.), он показывает откуда идти и какой ожидается результат. Далее строим дизайн с помощью unit/integration тестов, начиная с причины (ui, сообщение в сервис пришло, появилась запись в логе и т.п.) и заканчивая результатом (запись в базе, послали сообщение сервису, записали в лог и т.п.). Естественно, рефакторим в процессе и когда end-to-end зеленый. Работает отлично. Некоторые отделяют TDD (только модульные тесты) от ATDD/BDD (TDD + acceptance tests), но суть это не меняет.
Здравствуйте, -VaS-, Вы писали:
VS>В TDD/BDD используется несколько уровней тестов
Используются. Но обязательны в TDD только функциональные.
VS> и пишутся они outside-in — от end-to-end/acceptance через integration к unit. Пишем красный end-to-end/acceptance тест, описывающий (тестирующий) фичу (cucumber, fitnesse и т.д.)
Можно по русски?
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, michael_isu, Вы писали:
_>Тут вдруг неожиданно сам по себе реализуется SRP
Это очень вряд ли. Unit-тесты, в лучшем случае, поспособствуют низкой связности. Но вот хороший уровень зацепления (и SRP, как следствие) автоматично из-за наличия тестов не получится.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, gandjustas, Вы писали:
G>2)Unit-тесты абсолютно нет смысла писать после кода
G>Если вы создавая приложение выделяете некоторую стабильную часть кода, которую редко затрагивают при изменении требований, и которая используется другими частями приложения, то её нужно покрывать unit-тестами.
Проблема только в том, что стабильность участков кода обычно определяется уже после их написания, что несколько противоречит твоему п.2.
G>Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты.
Интеграционные тесты не делаются для отдельного модуля. По определению.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
G>>2)Unit-тесты абсолютно нет смысла писать после кода
G>>Если вы создавая приложение выделяете некоторую стабильную часть кода, которую редко затрагивают при изменении требований, и которая используется другими частями приложения, то её нужно покрывать unit-тестами.
AVK>Проблема только в том, что стабильность участков кода обычно определяется уже после их написания, что несколько противоречит твоему п.2.
Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. Как некоторый framework, на основе которого будет строиться приложение.
G>>Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты.
AVK>Интеграционные тесты не делаются для отдельного модуля. По определению.
Да, они делаются для модуля и некоторого подмножества связанных с ним. Обычно для самого модуля+вызываемых модулей из низлежащих слоев.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, michael_isu, Вы писали:
_>>Тут вдруг неожиданно сам по себе реализуется SRP
AVK>Это очень вряд ли. Unit-тесты, в лучшем случае, поспособствуют низкой связности. Но вот хороший уровень зацепления (и SRP, как следствие) автоматично из-за наличия тестов не получится.
Если бы делал TDD то знал бы что жто так и есть. Там где SRP нарушается начинает расти количество тестов.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
G>>Если бы делал TDD то знал бы что жто так и есть. AVK>Намекаешь на мою некомпетентность? А если без перехода на личности?
Уверен что ты не юзал TDD по полной программе в реальном проекте.
G>> Там где SRP нарушается начинает расти количество тестов. AVK>Магическим образом?
Нет, вполне естественным. Нарушение SRP приводит к тому что варианты поведения множатся при увеличении тех самых обязанностей. Это фактически приводит к росту количества и сложности тестов.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
G>>Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. AVK>Ты веришь в то, что это возможно?
Да, вполне бывает и так. Но редко и кода в таком "ядре" довольно мало.
G>> Как некоторый framework, на основе которого будет строиться приложение. AVK>А если ошибся? Таки писать тесты после?
Также как и при написании с нуля, только с другими предположениями.
Здравствуйте, gandjustas, Вы писали:
G>>>Если бы делал TDD то знал бы что жто так и есть. AVK>>Намекаешь на мою некомпетентность? А если без перехода на личности? G>Уверен что ты не юзал TDD по полной программе в реальном проекте.
Так, вежливый намек не понят. Тогда более прозрачно — заканчивай демагогию с переходом на личности. Есть что сказать по теме — говори, а компетенцию мою обсуждать не стоит, она, как минимум, не хуже твоей. И, внезапно, это еще и правилами форума запрещено.
G>>> Там где SRP нарушается начинает расти количество тестов. AVK>>Магическим образом? G>Нет, вполне естественным. Нарушение SRP приводит к тому что варианты поведения множатся при увеличении тех самых обязанностей. Это фактически приводит к росту количества и сложности тестов.
Ничего не понятно. Давай по другому. SRP это, по сути, иначе сформулированное требование низкой связности и высокого зацепления. С тестами и низкой связностью более менее понятно — тесты требуют формализации контрактов окружения, чтобы заткнуть внешний функционал моками. В результате это приводит к устранению некоторых видов высокой связности (не всех, но тем не менее). А вот с высоким зацеплением лично мне непонятно — каким образом тесты автоматично его обеспечивают? Попробуй объяснить без упоминания моей квалификации и прочей демагогии.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, gandjustas, Вы писали:
G>>>Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. AVK>>Ты веришь в то, что это возможно? G>Да, вполне бывает и так.
Ну, с верой не поспоришь.
G> Но редко и кода в таком "ядре" довольно мало.
Что редко? Стабильная часть определяется? Очень интересно.
AVK>>А если ошибся? Таки писать тесты после? G>Также как и при написании с нуля, только с другими предположениями.
Не понял. Итак, у нас на каком то этапе сформировалась эволюционным путем (ага, TDD и agile частенько ходят парой) стабильная область. Как обеспечить ее покрытие тестами? Заранее покрывать тестами все подозрительные области, или все таки обеспечивать покрытие постфактум? Или просто забить?
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
G>>>> Там где SRP нарушается начинает расти количество тестов. AVK>>>Магическим образом? G>>Нет, вполне естественным. Нарушение SRP приводит к тому что варианты поведения множатся при увеличении тех самых обязанностей. Это фактически приводит к росту количества и сложности тестов.
AVK>Ничего не понятно. Давай по другому. SRP это, по сути, иначе сформулированное требование низкой связности и высокого зацепления.
Это демагогия, ни SRP, ни "требование низкой связности и высокого зацепления" формализовать невозможно. Кстати про связность у нас DIP.
Нарушения SRP можно по косвенным признакам выявить.
AVK>А вот с высоким зацеплением лично мне непонятно — каким образом тесты автоматично его обеспечивают?
Тесты сами по себе ничего не обеспечивают, но они являются отличным индикатором нарушения SRP.
Простые примеры нарушения SRP.
1) Класс фактически имеет несколько набора методов, которые не связаны друг с другом. Для всех них требуются некоторые внешние кассы, которые заменяются моками. По тестам видно что не везде подсовываются все моки или то что подсовываются пустые.
2)Класс сочетает в себе несколько слоев абстракции, некоторые методы класса реализованы через публичные методы того же класса. По тестам сразу видно что для разных методов получаются почти одинаковые тесты.
3)Класс имеет очень много обязанностей и реализует их все в одном методе. По тестам видно что проверяемые результаты не связаны между собой никак. Такой случай после рефакторинга превращается в сочетание 1) и 2)
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
G>>>>Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. AVK>>>Ты веришь в то, что это возможно? G>>Да, вполне бывает и так. AVK>Ну, с верой не поспоришь.
Хз, я вживую такое видел.
G>> Но редко и кода в таком "ядре" довольно мало. AVK>Что редко? Стабильная часть определяется? Очень интересно.
Редко можно заранее стабильную часть выделить. Обычно это случается когда задача уже хорошо изучена.
AVK>>>А если ошибся? Таки писать тесты после? G>>Также как и при написании с нуля, только с другими предположениями.
AVK>Не понял. Итак, у нас на каком то этапе сформировалась эволюционным путем (ага, TDD и agile частенько ходят парой) стабильная область. Как обеспечить ее покрытие тестами?
Она уже будет покрыта тестами, если вообще пишутся тесты. А если нет, то разговор ни о чем.
Здравствуйте, gandjustas, Вы писали:
AVK>>Ничего не понятно. Давай по другому. SRP это, по сути, иначе сформулированное требование низкой связности и высокого зацепления. G>Это демагогия
Почему?
G>ни SRP, ни "требование низкой связности и высокого зацепления" формализовать невозможно
А вот приписывание мне утверждений, которые я никогда не делал — это демагогия. Где я писал хоть что то про формализацию?
G>. Кстати про связность у нас DIP.
Чего?
G>Нарушения SRP можно по косвенным признакам выявить.
И что?
AVK>>А вот с высоким зацеплением лично мне непонятно — каким образом тесты автоматично его обеспечивают? G>Тесты сами по себе ничего не обеспечивают
Да? А началось все с утверждения, что, цитирую:
Тут вдруг неожиданно сам по себе реализуется SRP и прочий SOLID, и вообще ООП
Я так понимаю, что вопросы с твоей стороны сняты?
И, кстати, низкую связность в ряде случаев они таки сами по себе обеспечивают.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, gandjustas, Вы писали:
G>>> Но редко и кода в таком "ядре" довольно мало. AVK>>Что редко? Стабильная часть определяется? Очень интересно. G>Редко можно заранее стабильную часть выделить.
Я бы сказал — практически никогда, если это не 100500-й проект на одну и ту же тему. Тем интереснее становятся твои рекомендации, вырождающиеся, по сути, в рекомендации юнит-тесты делать только в случае "если ваш код сильно нетривиален и по интерфейсу реализация совсем не очевидна".
AVK>>Не понял. Итак, у нас на каком то этапе сформировалась эволюционным путем (ага, TDD и agile частенько ходят парой) стабильная область. Как обеспечить ее покрытие тестами? G>Она уже будет покрыта тестами, если вообще пишутся тесты
Цитирую:
Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты.
Откуда юнит-тесты возьмутся?
G>. А если нет, то разговор ни о чем.
Попробуй ответить на заданный вопрос. Я там три варианта привел — выбери какой нибудь или свой предложи.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
AVK>>>Ничего не понятно. Давай по другому. SRP это, по сути, иначе сформулированное требование низкой связности и высокого зацепления. G>>Это демагогия
AVK>Почему?
G>>ни SRP, ни "требование низкой связности и высокого зацепления" формализовать невозможно
AVK>А вот приписывание мне утверждений, которые я никогда не делал — это демагогия. Где я писал хоть что то про формализацию?
G>>. Кстати про связность у нас DIP.
AVK>Чего?
G>>Нарушения SRP можно по косвенным признакам выявить.
AVK>И что?
AVK>>>А вот с высоким зацеплением лично мне непонятно — каким образом тесты автоматично его обеспечивают? G>>Тесты сами по себе ничего не обеспечивают
Единица текста на русском языке — одно предложение, иногда абзац. Комментируя по отдельности каждое слово конструктив только теряется.
AVK>Да? А началось все с утверждения, что, цитирую: AVK>
AVK>Тут вдруг неожиданно сам по себе реализуется SRP и прочий SOLID, и вообще ООП
Это я писал? Что ты понял под фразой "сам по себе"? Магические преобразования кода?
Я всю фразу понимаю как "применение TDD подталкивает к принципам SOLID", это и согласуется с моим опытом.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, gandjustas, Вы писали:
G>>>> Но редко и кода в таком "ядре" довольно мало. AVK>>>Что редко? Стабильная часть определяется? Очень интересно. G>>Редко можно заранее стабильную часть выделить.
AVK>Я бы сказал — практически никогда, если это не 100500-й проект на одну и ту же тему.
Я бы так не сказал.
Примеры:
1) бухгалтерия — счета, остатки, обороты. Над ними уже накручивается остальная логика.
2) Физический\графический движок
3) CMS
Это то, с чем я имел дело.
AVK>Тем интереснее становятся твои рекомендации, вырождающиеся, по сути, в рекомендации юнит-тесты делать только в случае "если ваш код сильно нетривиален и по интерфейсу реализация совсем не очевидна".
См примеры выше. Я сталкивался и сам разрабатывал подобные фреймворки. Довольно выгодно иметь в них хорошее покрытие тестами как success, так и failure.
AVK>>>Не понял. Итак, у нас на каком то этапе сформировалась эволюционным путем (ага, TDD и agile частенько ходят парой) стабильная область. Как обеспечить ее покрытие тестами? G>>Она уже будет покрыта тестами, если вообще пишутся тесты
AVK>Цитирую: AVK>
AVK>Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты.
AVK>Откуда юнит-тесты возьмутся?
А я не говорю возьмутся. Пусть любые другие будут.
G>>. А если нет, то разговор ни о чем.
Ну если так считаешь, то и не стоило писать. Ты вместо конструктива пишешь непонятно что, засоряя тему ненужным текстом. Лучше бы что-нить по теме написал.
Подписываюсь под каждым сообщением gandjustas. DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Я пишу юнит тесты только для сложной логики. Еще часто бывает, что тест написать намного проще чем логику. В таких случаях модульные тесты писать категорически рекомендуется.
Писать же юнит тесты для метода на 10 мин смысла не имеет никакого. Лучше написать интеграционный тест сразу на весь/все сценарии которые ты разрабатываешь в данным момент.
Юнит тесты так же сильно помогают при последующем рефакторинге или модификации класса в связи с изменившимися требованиями. Уузнать когда тесты понадобятся когда нет -- невозможно, но с опытом можно уже пытаться угадывать. С др стороны, сложная логика скорее всего (у меня) будет покрыта тестами, а простая или сложная но без тестов рефакториться с мыслями "сто раз так делал"
Примеры из моего опыта:
Пример сложной логики, тесты простые, но их достаточно много (14 штук):
Скрытый текст
Private _shiftPattern As ShiftPattern() = New ShiftPattern() {New ShiftPattern("A", "06:00", "14:00"), New ShiftPattern("B", "14:00", "22:00"), _
New ShiftPattern("C", "22:00", "06:00")}
<Test()> _
Public Sub Can_Recognize_If_Current_Shifts_In_The_Middle()
Dim mgr = New ShiftsManager(_shiftPattern, Function() Date.Parse("15:00"))
Assert.That(mgr.PrevShift.Name, Iz.EqualTo("A"))
Assert.That(mgr.CurrentShift.Name, Iz.EqualTo("B"))
Assert.That(mgr.NextShift.Name, Iz.EqualTo("C"))
End Sub
<Test()> _
Public Sub Can_Recognize_If_Current_Shifts_Is_First()
Dim mgr = New ShiftsManager(_shiftPattern, Function() Date.Parse("7:00"))
Assert.That(mgr.PrevShift.Name, Iz.EqualTo("C"))
Assert.That(mgr.CurrentShift.Name, Iz.EqualTo("A"))
Assert.That(mgr.NextShift.Name, Iz.EqualTo("B"))
End Sub'...
<Test()> _
Public Sub Can_Calculate_Prev_Next_Shifts_Start_Stop_If_Current_Shifts_Is_Last_And_Time_Before_Midnight()
Dim mgr = New ShiftsManager(_shiftPattern, Function() Date.Parse("23:00"))
Assert.That(mgr.CurrShiftStart, Iz.EqualTo(Date.Parse("22:00")), "CurrShiftStart")
Assert.That(mgr.CurrShiftEnd, Iz.EqualTo(Date.Parse("6:00").AddDays(1)), "CurrShiftEnd")
Assert.That(mgr.PrevShiftStart, Iz.EqualTo(Date.Parse("14:00")), "PrevShiftStart")
Assert.That(mgr.NextShiftEnd, Iz.EqualTo(Date.Parse("14:00").AddDays(1)), "NextShiftEnd")
End Sub
Еще бывают случаи, когда логика простая, но ты знаешь, что тебе в ней ошибиться как два пальца. Например, я всегда путаюсь в кол-ве символов для substring (сколько символов вырезается или +-1?).
Для таких случаев у меня есть класс QuickTests с единственным пустым методом. Туда побыструхе пишется тест, прогоняется до "зелености" и тут же удаляется.
Есть случаи когда тест уж очень простой, логика -- отностиельно проста (смысл теста -- застраховать тебя от тупых ошибок). Пример -- нужно распарсить xml-ответ от внешнего сервиса. Тест написать элементарно (скопипастил ответ из браузера или SoapUI). Глупую ошибку сделать очень легко. Тест после прохождения либо удаляется либо остается для предотвращения ошибок регрессиии.
Это все что касается Юнит тестов. Есть еще примеры, но они совсем не "Юнит".
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
По моему опыту юнит тесты хороши при написании кода.
Я для себя пришел к выводу, что лучше всего писать одновременно и функционал и тесты к нему. Это способствует лучшему пониманию задачи и более четкому выделению контрактов классов (а это очень важно).
Писать же тесты полностью (например сразу несколько тестов для всего класса, при этом его контракт еще не до конца понятен) до написания кода плохо. Потому что на этапе поиска оптимального решения постоянно изменяются контракты, следовательно переписываются тесты.
Ну, и ошибки регрессии они тоже помогают отловить.
Здравствуйте, AndrewVK, Вы писали:
AVK>Да? А началось все с утверждения, что, цитирую: AVK>
AVK>Тут вдруг неожиданно сам по себе реализуется SRP и прочий SOLID, и вообще ООП
Имелось ввиду, что проблемы со связностью и связанностью при написании тестов так и начинают сразу зиять, что сильно способствует написанию качественного кода сразу же. Без тестов для меня, например, эти проблемы часто неочевидны и всплывают только позже, при развитии проекта.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, michael_isu, Вы писали:
_>>Тут вдруг неожиданно сам по себе реализуется SRP
AVK>Это очень вряд ли. Unit-тесты, в лучшем случае, поспособствуют низкой связности. Но вот хороший уровень зацепления (и SRP, как следствие) автоматично из-за наличия тестов не получится.
Хороший уровень зацепления получится потому, что слишком уж много тестов придется написать, чтобы покрыть множество кейсов работы класса, если он большой и сложный, и когда эти тесты начинают писаться, то приходит понимание, что все это слишком сложно и трудоемко, это понимание _подталкивает_ выделять разные ответственности в разные классы, чтобы их проще было протестировать. В итоге — разные ответственности в разных классах и каждый из них сфокусирован на одной обязанности -> high cohesion.
Здравствуйте, gandjustas, Вы писали:
AVK>>Цитирую: AVK>>
AVK>>Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты.
AVK>>Откуда юнит-тесты возьмутся? G>А я не говорю возьмутся.
Вот именно что не говоришь. А я именно об этом и спрашиваю уже третье сообщение.
G>Ну если так считаешь, то и не стоило писать. Ты вместо конструктива пишешь непонятно что, засоряя тему ненужным текстом. Лучше бы что-нить по теме написал.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, Flem1234, Вы писали:
F>По моему опыту юнит тесты хороши при написании кода. F>Я для себя пришел к выводу, что лучше всего писать одновременно и функционал и тесты к нему. Это способствует лучшему пониманию задачи и более четкому выделению контрактов классов (а это очень важно). F>Писать же тесты полностью (например сразу несколько тестов для всего класса, при этом его контракт еще не до конца понятен) до написания кода плохо. Потому что на этапе поиска оптимального решения постоянно изменяются контракты, следовательно переписываются тесты.
+2. Требование писать тесты до кода — фактически приём заставить даже чрезмерно ленивых всё-таки сделать эти тесты, но это требование помогают против ленивых, а нормальным мешает. Чуть подробнее об этом требовании и TDD в целом.
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Во-вторых, в TDD скорее функциональные тесты основную рояль играют, а не юнит. N>>Это скорее справедливо для behavior-driven design (BDD)
AVK>Согласно википедии: AVK>
AVK>It extends TDD by writing test cases in a natural language that non-programmers can read. Behavior-driven developers use their native language in combination with the ubiquitous language of domain driven design to describe the purpose and benefit of their code. This allows the developers to focus on why the code should be created, rather than the technical details, and minimizes translation between the technical language in which the code is written and the domain language spoken by the business, users, stakeholders, project management, etc.
AVK>Так что вряд ли. BDD, получается, ближе к классическому водопадному подходу, где юзкейсы описываются на естественном языке или UML.
Ничего водопадного в процитированном куске нет, и в других нет. Водопад получается от совершенно независимого фактора — что описание необходимого функционала происходит гарантированно до разработки. В этом случае, да, ТЗ напрямую переводится в описание BDD и дальше не меняется. Но никто не требует от BDD, чтобы их описание не менялось. Сам по себе подход BDD родился в Agile (не хочу копать, в каком именно), а значит, сами спецификации необходимого могут корректироваться по необходимости. UML тут обычно ни при чём, а реально используется какой-то DSL, близкий к естественному языку. Тут, например, такой DSL строится поверх синтаксиса Ruby, но уже пригоден для написания тестером без глубокого знания языка.
N>>, там основные тесты — именно функциональные на целевые действия. А TDD такого ограничения не предполагает, скорее наоборот — в полном виде оно требует такого контроля на мельчайшие детали разработки.
AVK>Согласно википедии: AVK>
AVK>Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards.
AVK>Так что TDD все таки функциональные тесты предполагает в обязательном порядке. А unit уже по желанию.
Это с чего такой вывод? Во-первых, там нет ни слова про functional, integration или что-то подобное, там только test case. Во-вторых, если _a very short_ development cycle, то он должен повторяться на каждую функцию или небольшой модуль, а это само по себе предполагает юнит-тесты задолго до функциональных.
Да, граница между юнит-тестами и функциональными тестами нестрога; в некоторых случаях это одно и то же, как, например, при проверке относительно простой библиотечной функции. Но, например, для моей текущей работы эта разница в том, что юнит-тесты это проверка на коде, а функциональные — на живых процессах (запускается приложение, получает поток данных, отдаёт поток данных), и тут можно жёстко разделять их. Функциональные тесты являются основным проверяемым фактором для QA — наш QA про юнит-тесты ничего не знает, это дело программистов и меня как начальника группы программистов.
AVK> Что, помимо прочего, еще и логично, так как основная идея TDD состоит в описании ТЗ (то бишь функционала) в виде тестов,
Некорректный логический переход. ТЗ на функцию, да, описывает её функционал, но понятие функциональных тестов подразумевает проверку логически завершённого модуля, а не каждой функции (что даже мельче того "юнита", из которого родилось понятие unit testing).
TDD в распространённых описаниях требует, чтобы на каждое, требующее проверки (начиная с функции), рисовался тест до кода. Я уже упоминал идеологическую дырявость этого подхода, хотя если в твоём подчинении банда ленивых ламеров, он может оказаться единственным успешно работающим (к счастью, с таким пока не сталкивался).
AVK> что как раз таки функциональные тесты и обеспечивают, а никак не unit, которые зависят от конкретного, уже готового дизайна приложения. Скорее всего, попутал ты TDD с XP. Вот последнее как раз таки именно на unit-тестирование ориентировано.
Я ничего не путал, а твои методы парадоксальны — ты добавляешь предположения, нигде не описанные, и получаешь совершенно неожиданный результат. Непонятно, почему и зачем ты так делаешь.
Википедия, BTW, далеко не единственный источник. В описании этих методик я больше опирался на статьи с хабра, чем на википедию, и считаю это в данном случае более разумным.
N>>(Да, я понимаю, что у каждого свой источник знаний. Но я массово видел именно такое различие) AVK>Википедия — достаточно массово?
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Pzz, Вы писали:
Pzz>>Говорят, они хороши, когда уже сильно потом класс ковыряешь. Подковырнул, тесты-то и посыпались, сидишь, чешешь репу
AVK>Когда сильно ковыряешь, unit-тесты очень плохи, так как висят свинцовой гирей и мешают проводить глубокий рефакторинг.
Тесты -- это код. Любой код висит свинцовой гирей, когда "сильно ковыряешь".
AVK> А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне.
Мы все еще про юнит-тесты говорим? Тогда о каких пачках идет речь? (отвечать не нужно: Когда тестов много, они все равно разбиты на 2-3 группы внутри которых тесты похожи как близнецы -- менять их не сложно)
Юнит-тесты -- тесты на один класс. В основном. Даже если тестируется несколько классов, то всегда есть, скажем, так "центральный класс" (ЦК). Все остальные классы напрямую не тестируются, только через интерфейс "центрального класса".
Если требования влекут изменения интерфейса "центрального класса", то обычно достаточно подправить тесты в соответствии с новым интерфейсом. И все опять "зазелененло". В особо сложных случаях интерфейс и тесты меняются итеративно (в конце каждой итерации все должно "зеленеть").
Если же требования меняются "ну просто пипец", то, может, проще выкинуть нафик эту иерархию (начиная с ЦК) вместе с тестами и переписать заново?
Еще одна мысль к размышлению:
Тесты -- это требования к классу.
Когда требования меняются, часть устаревает (соответственно устаревают соответствующие тесты -- удаляем их), но обычно большая часть требований остается. Сохранить соответствующие тесты будет отличной идеей. Даже не так: удалить соответствующие тесты будет крайне глупой идеей.
Pzz>>А иначе придется еще и тесты ковырять, не только класс. Pzz, вас же не смущает, что при изменении класса код соседних так же обычно приходится ковырять? Воспринимайте тесты как код. Полезный код. И все будет ОК.
Здравствуйте, AndrewVK, Вы писали:
Pzz>>Говорят, они хороши, когда уже сильно потом класс ковыряешь. Подковырнул, тесты-то и посыпались, сидишь, чешешь репу :-) AVK>Когда сильно ковыряешь, unit-тесты очень плохи, так как висят свинцовой гирей и мешают проводить глубокий рефакторинг. А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне.
И что с того? Естественно, что рефакторинг что-то меняет. И если связи между компонентами (неважно, какого уровня) меняются, то и тесты на специфическую реализацию этих связей ломаются и требуют исправления. Но если по этому поводу плакать, то получается, что вообще ничего кроме самого внешнего функционала не надо тестировать, потому что всё остальное может меняться. Так, что ли?
Написание тестов — такая же работа, как и остальное, и должно быть распределено адекватно по реальной необходимости. Покрывать сплошным ковром юнит-тестов весь код — такая же нелепость, как и писать для функционального теста перебор, например, по третьей букве имени в акаунте — типа, а вдруг где-то что-то сработает не так. Но если есть кусок нетривиального кода в виде одной функции — на него нужен юнит-тест, и если этот код вылетит на следующем рефакторинге — селяви, но заранее плакаться по нему смысла не имеет. Где-то может быть ни одного юнит-теста — например, они вряд ли нужны для класса, который просто контейнер свойств без заметного набора данных. Где-то, наоборот, всё может быть ими покрыто в три слоя (у меня такие — математика, переработка данных, конверсия сложных структур). Где-то самые важные тесты — нагрузочные, а функционально тестировать нечего, или же нагрузочный тест включает в себя функциональный. Общего рецепта не будет (и ещё и потому плох TDD, что на некоторых случаях увеличивает работу в надцать раз без полезного выхлопа).
Pzz>>А иначе придется еще и тесты ковырять, не только класс. AVK>Вот именно.
И ничего в этом плохого нет. Если тест сломался, значит, его зацепило.
В наиболее ответственных частях я делаю (см. ссылку из прошлого письма) методы проверки собственно теста, потому что бывают и такие перемены основного кода, что проверка в тесте перестаёт срабатывать и всегда оказывается положительной. Такие расширенные тесты (когда создаётся обстановка для поломки или заметного изменения основного теста) пускаются разработчиками соответствующих систем или мной, но QA с ними уже не связывается (пока что).
А есть ещё самоконтроль в рантайме и поддержка ручного контроля там же, но это отдельная тема.
Здравствуйте, gandjustas, Вы писали:
G>>>2)Unit-тесты абсолютно нет смысла писать после кода G>>>Если вы создавая приложение выделяете некоторую стабильную часть кода, которую редко затрагивают при изменении требований, и которая используется другими частями приложения, то её нужно покрывать unit-тестами. AVK>>Проблема только в том, что стабильность участков кода обычно определяется уже после их написания, что несколько противоречит твоему п.2. G>Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. Как некоторый framework, на основе которого будет строиться приложение.
Здесь AndrewVK более прав: определение стабильной части кода заранее на практике скорее невозможно, а там, где возможно, она уже стабилизировалась давно (и обычно выглядит в виде библиотеки) и требует наличия тестов уже до текущей работы.
Твои примеры с бухгалтерией, физическим или графическим движком мне кажутся неадекватными. В них есть много тонкостей, которые могут на второй уже версии сильно меняться. Например, следующий DirectX, или требование пересчёта баланса впараллель на нескольких ядрах вместо текущего одного ядра могут переломать всю архитектуру слоя поддержки.
Поэтому разумно просто отвести ресурсы на написание тестов, не конкретизируя, какие именно они должны быть. Где-то это юнит-тесты до каждой строчки кода, где-то — функциональный на группу классов. Осталось, чтобы руководитель согласился с критериями разумности.
G>>>Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты. AVK>>Интеграционные тесты не делаются для отдельного модуля. По определению. G>Да, они делаются для модуля и некоторого подмножества связанных с ним. Обычно для самого модуля+вызываемых модулей из низлежащих слоев.
Для меня это будет называться, скорее всего, функциональными тестами (а интеграционными будут проверки суммарной функциональности нескольких крупных блоков, не подчинённых друг другу, а работающими совместно).
Здравствуйте, AndrewVK, Вы писали:
G>>2)Unit-тесты абсолютно нет смысла писать после кода G>>Если вы создавая приложение выделяете некоторую стабильную часть кода, которую редко затрагивают при изменении требований, и которая используется другими частями приложения, то её нужно покрывать unit-тестами. AVK>Проблема только в том, что стабильность участков кода обычно определяется уже после их написания, что несколько противоречит твоему п.2.
Вот уж придрался так придрался. По остальным пунктам вопросов похоже нет.
Из сообщения ниже: G>>Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. AVK>Ты веришь в то, что это возможно?
Я, лично, верю в то, что не ошибатется только тот кто ничего не делает.
Давай поговорим не про тестирование, а про проектирование. Озвученый вопрос напрямую относится к проектированию.
При проектировании ты делаешь какие-то предположения и закладываешься на них. Если твои предположения окажутся ложными тебе придеться существенно менять архитектуру. Для борьбы с этим архитектуру дробят на компоненты. Если придет писец, то придется не все переписывать, а только один компонент. Чем опытнее архитектор, тем меньше приходится переписывать в случае чего.
Но перепиывать приходится все равно. Это, почему-то, никого не смущает, типа "такова жизнь".
То же самое для тестов. Не угадал -- переписывай. Чем больше опыта у разработчика -- тем меньше приходится перепиывать. Тем более, что переписывать нужно будет только тесты для изменяемого модуля (поэтому тесты без хорошей архитектуры и дизайна действительно обуза).
G>>Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты. AVK>Интеграционные тесты не делаются для отдельного модуля. По определению.
Интеграционные тесты тестируют в том числе и конкретные модули. Именно это хотел сказать и сказал gandjustas.
Здравствуйте, netch80, Вы писали:
AVK>>Так что TDD все таки функциональные тесты предполагает в обязательном порядке. А unit уже по желанию.
N>Это с чего такой вывод? Во-первых, там нет ни слова про functional, integration или что-то подобное, там только test case.
С того, что "test case that defines a desired improvement or new function" это функциональный тест. По определению:
Functional testing refers to activities that verify a specific action or function of the code. These are usually found in the code requirements documentation, although some development methodologies work from use cases or user stories. Functional tests tend to answer the question of "can the user do this" or "does this particular feature work."
N>Да, граница между юнит-тестами и функциональными тестами нестрога; в некоторых случаях это одно и то же, как, например, при проверке относительно простой библиотечной функции. Но, например, для моей текущей работы эта разница в том, что юнит-тесты это проверка на коде, а функциональные — на живых процессах (запускается приложение, получает поток данных, отдаёт поток данных)
Тестирование на живых процессах, когда запускается приложение целиком, это интеграционное тестирование. Функциональное точно так же тестирует в основном отдельные модули. Разница с unit в том, что последнее тестирует все аспекты контракта, а функциональное проверяет работоспособность модуля на предполагаемых юзкейсах. Т.е. unit-тестирование проверяет соответствие контракта спецификации, а функциональное — соответствие функционала входному ТЗ.
N>Функциональные тесты являются основным проверяемым фактором для QA — наш QA про юнит-тесты ничего не знает, это дело программистов и меня как начальника группы программистов.
Это вопрос организационный, к разделению на юнит и функциональное это никакого отношения не имеет. Функциональные тесты очень часто пишутся именно программистами.
N>Некорректный логический переход. ТЗ на функцию, да, описывает её функционал, но понятие функциональных тестов подразумевает проверку логически завершённого модуля
Ничего подобного оно не подразумевает. См. определение.
N>Википедия, BTW, далеко не единственный источник.
О да, знакомо. Если что то не стыкуется — виновата википедия.
Не, она, конечно, далеко не единственный и даже не самый авторитетный источник, но если выбирать между ней и твоими голословными заявлениями, то я предпочту первое.
N> В описании этих методик я больше опирался на статьи с хабра
О! Зашибись источник. Все с вами ясно.
N>, чем на википедию, и считаю это в данном случае более разумным.
Считай.
AVK>>Википедия — достаточно массово? N>Нет.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, Aikin, Вы писали:
AVK>>Когда сильно ковыряешь, unit-тесты очень плохи, так как висят свинцовой гирей и мешают проводить глубокий рефакторинг. A>Тесты -- это код. Любой код висит свинцовой гирей, когда "сильно ковыряешь".
Большое количество тестов "всего лишь" увеличивает его количество.
AVK>> А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне. A>Мы все еще про юнит-тесты говорим? Тогда о каких пачках идет речь?
Об обычных. Глубокий рефакторинг ломает конктракты — ломаются все связанные с этим тесты.
A> (отвечать не нужно
Сам с собою?
A>: Когда тестов много, они все равно разбиты на 2-3 группы внутри которых тесты похожи как близнецы -- менять их не сложно)
Может и не сложно, но это таки лишняя работа.
A>Юнит-тесты -- тесты на один класс. В основном.
Я в курсе.
A>Если требования влекут изменения интерфейса "центрального класса", то обычно достаточно подправить тесты в соответствии с новым интерфейсом. И все опять "зазелененло".
А потом оказалось, что надо еще подрихтовать. И опять тесты правим. А потом еще.
В результате либо мы делаем кучу обезьянней работы, либо пишем тесты в конце рефакторинга, что как бы не комильфо.
A> В особо сложных случаях интерфейс и тесты меняются итеративно (в конце каждой итерации все должно "зеленеть").
Во во.
A>Еще одна мысль к размышлению: A>Тесты -- это требования к классу.
Функциональные тесты — да, требования. А юнит — всего лишь контроль контракта. Тоже вроде как требования, только актуальные в рамках конкретного готового дизайна, а не входных задач. Дизайн меняется (даже без изменения входных требований) — конкретные спецификации становятся неактуальны.
Pzz>>>А иначе придется еще и тесты ковырять, не только класс. A>Pzz, вас же не смущает, что при изменении класса код соседних так же обычно приходится ковырять? Воспринимайте тесты как код. Полезный код. И все будет ОК.
Т.е. надо просто мантру прочесть и поверить?
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, netch80, Вы писали:
AVK>>Когда сильно ковыряешь, unit-тесты очень плохи, так как висят свинцовой гирей и мешают проводить глубокий рефакторинг. А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне.
N>И что с того?
Дополнительная работа.
N> Естественно, что рефакторинг что-то меняет. И если связи между компонентами (неважно, какого уровня) меняются, то и тесты на специфическую реализацию этих связей ломаются и требуют исправления. Но если по этому поводу плакать, то получается, что вообще ничего кроме самого внешнего функционала не надо тестировать, потому что всё остальное может меняться. Так, что ли?
Нет, не так. Доводение до абсурда доказывает лишь то, что все можно довести до абсурда.
Pzz>>>А иначе придется еще и тесты ковырять, не только класс. AVK>>Вот именно. N>И ничего в этом плохого нет. Если тест сломался, значит, его зацепило.
Вопрос лишь в адекватности этого зацепления. В большинстве случаев, при сильном рефакторинге, я и так в курсе, какие тесты и почему сломаются до их запуска. Т.е. их срабатывание не приносит никакой пользы, и никто их не чинит, их либо основательно переписывать надо сразу, до запуска, либо вообще выкидывать, если тестируемый ими контракт изменился до неузнаваемости.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>А потом оказалось, что надо еще подрихтовать. И опять тесты правим. А потом еще. AVK>В результате либо мы делаем кучу обезьянней работы, либо пишем тесты в конце рефакторинга, что как бы не комильфо.
Вот прочитаешь такую фразу и думаешь, а ведь все верно. Но только для того кто действительно пишущего тесты становитсья понятно, что работы там совсем не куча, а максимум перегруппировать параметры методов, добавить новые, подправить инициализацию класса (тем более, что крупные изменения будут разбиты на шаги, многие из которых автоматизированы Решарпером).
Работы не много, а результатом ее будет гарантия что ты своим новым интерфейсом ничего не поломал.
80% нашей работы -- обезьянья. И чо теперь? Не работать?
A>>Еще одна мысль к размышлению: A>>Тесты -- это требования к классу.
AVK>Функциональные тесты — да, требования. А юнит — всего лишь контроль контракта. Тоже вроде как требования, только актуальные в рамках конкретного готового дизайна, а не входных задач. Дизайн меняется (даже без изменения входных требований) — конкретные спецификации становятся неактуальны.
Именно про требования "в рамках конкретного готового дизайна" я и говорю. Что не так?
AVK>>> А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне. A>>Мы все еще про юнит-тесты говорим? Тогда о каких пачках идет речь? AVK>Об обычных. Глубокий рефакторинг ломает конктракты — ломаются все связанные с этим тесты.
Любой рефакторинг идет итеративно, небольшими шажками. Тесты отваливаются постепенно. В конце каждой итерации зеленыее тесты поддтверждат корректность текущего шага.
Я вообще не представляю как можно делать рефакторинг в стиле "все похерим, а потом разберемся".
При "очень глубоком рефакторинге" классы вместе с тестами проще выкинуть, чем изменять.
И вообще, как часто системе необходим "глубоком рефакторинг" (и что это за зверь-то?)
A>>: Когда тестов много, они все равно разбиты на 2-3 группы внутри которых тесты похожи как близнецы -- менять их не сложно) AVK>Может и не сложно, но это таки лишняя работа.
Кому лишняя -- пусть не меняет тесты. Пусть вообще их не пишет. Для меня польза от этой "лишней работы" существенно превышает затраты.
Здравствуйте, Aikin, Вы писали:
A>Вот прочитаешь такую фразу и думаешь, а ведь все верно. Но только для того кто действительно пишущего тесты становитсья понятно, что работы там совсем не куча
Не распарсил
A>Работы не много, а результатом ее будет гарантия что ты своим новым интерфейсом ничего не поломал.
Т.е. главное — убедить себя, что работы немного. Понятно.
A>80% нашей работы -- обезьянья.
Говори за себя.
A> И чо теперь? Не работать?
Нет, учится уменьшать ее количество.
AVK>>Функциональные тесты — да, требования. А юнит — всего лишь контроль контракта. Тоже вроде как требования, только актуальные в рамках конкретного готового дизайна, а не входных задач. Дизайн меняется (даже без изменения входных требований) — конкретные спецификации становятся неактуальны. A>Именно про требования "в рамках конкретного готового дизайна" я и говорю. Что не так?
Не так — когда сам дизайн меняется. Юнит-тесты при этом большими кусками перестают выполнять свою работу. Всего лишь. Это, в свою очередь, удорожает рефакторинг. А дальше ты сам должен думать — стоит ли оно того. И нет, не путем уговаривания себя, что таки стоит, а путем вдумчивого взвешивания всех плюсов и минусов.
AVK>>Об обычных. Глубокий рефакторинг ломает конктракты — ломаются все связанные с этим тесты. A>Любой рефакторинг идет итеративно, небольшими шажками.
Не любой.
A>При "очень глубоком рефакторинге" классы вместе с тестами проще выкинуть, чем изменять.
Классы — нет, не проще. Тесты — да, проще. О чем и речь.
A>И вообще, как часто системе необходим "глубоком рефакторинг" (и что это за зверь-то?)
Нормативов нет, если ты об этом. Все зависит от потребностей.
AVK>>Может и не сложно, но это таки лишняя работа. A>Кому лишняя -- пусть не меняет тесты. Пусть вообще их не пишет. Для меня польза от этой "лишней работы" существенно превышает затраты.
Ага, главное — верить.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Когда сильно ковыряешь, unit-тесты очень плохи, так как висят свинцовой гирей и мешают проводить глубокий рефакторинг. А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне.
N>>И что с того? AVK>Дополнительная работа.
А дебагинг очень нетривиального бага внесенного рефакторингом без тестов это не доп. работа? А работа QA который нашел этот баг? А время и головную заказчика, который не смог сделать то что ему нужно или еще хуже сделал то, что ему не нужно ты учитываешь?
Pzz>>>>А иначе придется еще и тесты ковырять, не только класс. AVK>>>Вот именно. N>>И ничего в этом плохого нет. Если тест сломался, значит, его зацепило.
AVK>Вопрос лишь в адекватности этого зацепления. В большинстве случаев, при сильном рефакторинге, я и так в курсе, какие тесты и почему сломаются до их запуска. Т.е. их срабатывание не приносит никакой пользы, и никто их не чинит, их либо основательно переписывать надо сразу, до запуска, либо вообще выкидывать, если тестируемый ими контракт изменился до неузнаваемости.
Ценность тестов при рефакторинге не только в том, чтобы указать что именно ты сломал, но и в том, чтобы в конце ты все "вернул на место". Т.е. оставил логику в том же состоянии что и до рефакторинга. Это раз.
Второй момент заключается в том, что хоть ты и предполагаешь какие именно тесты упадут (не будем говорить про изменение контрактов, когда компилятор может указать на ошибки), не не можешь быть 100% уверен, что не упадет что-то еще или что некоторые тесты изменения не затронут.
Опять же мы не рассматриваем очевидные случаи. Для них тестов (юнит) не будет.
Здравствуйте, Aikin, Вы писали:
A>Ценность тестов при рефакторинге не только в том, чтобы указать что именно ты сломал, но и в том, чтобы в конце ты все "вернул на место". Т.е. оставил логику в том же состоянии что и до рефакторинга. Это раз.
Пошел по кругу. Это работает для юнит-тестов только если не меняются контракты. Т.е. при неглубоком рефакторинге, на уровне внутренней реализации одного класса. Речь же шла не об этом.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Функциональные тесты — да, требования. А юнит — всего лишь контроль контракта. Тоже вроде как требования, только актуальные в рамках конкретного готового дизайна, а не входных задач. Дизайн меняется (даже без изменения входных требований) — конкретные спецификации становятся неактуальны. A>>Именно про требования "в рамках конкретного готового дизайна" я и говорю. Что не так?
AVK>Не так — когда сам дизайн меняется. Юнит-тесты при этом большими кусками перестают выполнять свою работу. Всего лишь. Это, в свою очередь, удорожает рефакторинг.
Вывод не верный. Хоть и похоже на правду. Давай я свою "правду" озвучу:
Когда сам дизайн менятся, функциональность существующих классов переноситься в другие классы. Чем большее количество логики меняет свое местоположение тем проще ошибиться и сложнее убедиться в том, что мы ничего не сломали рефакторингом. Чтобы убедиться в правильности рефакторинга нужно делать его ооочень внимательно и осторожно. А после окончания нужно обязательно прогнать систему по основным сценариям. Выявятся баги (куда же без них). Чтобы локализовать баг и исправить причину приходится дебажить, иногда очень долго. Это, в свою очередь, удорожает рефакторинг.
AVK>А дальше ты сам должен думать — стоит ли оно того. И нет, не путем уговаривания себя, что таки стоит, а путем вдумчивого взвешивания всех плюсов и минусов.
Спасибо за совет К.О.
AVK>>>Об обычных. Глубокий рефакторинг ломает конктракты — ломаются все связанные с этим тесты. A>>Любой рефакторинг идет итеративно, небольшими шажками. AVK>Не любой.
Тогда удачи тебе в глубоких рефакторингах одним махом.
A>>При "очень глубоком рефакторинге" классы вместе с тестами проще выкинуть, чем изменять. AVK>Классы — нет, не проще. Тесты — да, проще. О чем и речь.
Юнит-тесты всегда значительно проще чем логика класса. Иначе в них практического смысла нет.
Если же тест сложнее класса, то (на это должна быть причина) класс этот отвечает за критическую функциональность.
AVK>>>Может и не сложно, но это таки лишняя работа. A>>Кому лишняя -- пусть не меняет тесты. Пусть вообще их не пишет. Для меня польза от этой "лишней работы" существенно превышает затраты. AVK>Ага, главное — верить.
Ага, главное -- не верить. Ведь ты мне не поверишь, если я скажу, что переодически обнаруживаю ошибки в коде после восстановления тестов.
Здравствуйте, AndrewVK, Вы писали:
A>>Ценность тестов при рефакторинге не только в том, чтобы указать что именно ты сломал, но и в том, чтобы в конце ты все "вернул на место". Т.е. оставил логику в том же состоянии что и до рефакторинга. Это раз. AVK>Пошел по кругу. Это работает для юнит-тестов только если не меняются контракты. Т.е. при неглубоком рефакторинге, на уровне внутренней реализации одного класса. Речь же шла не об этом.
Когда контракты не меняются тесты вообще не трогаются. О чем речь?
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, gandjustas, Вы писали:
G>>>>2)Unit-тесты абсолютно нет смысла писать после кода G>>>>Если вы создавая приложение выделяете некоторую стабильную часть кода, которую редко затрагивают при изменении требований, и которая используется другими частями приложения, то её нужно покрывать unit-тестами. AVK>>>Проблема только в том, что стабильность участков кода обычно определяется уже после их написания, что несколько противоречит твоему п.2. G>>Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. Как некоторый framework, на основе которого будет строиться приложение.
N>Здесь AndrewVK более прав: определение стабильной части кода заранее на практике скорее невозможно, а там, где возможно, она уже стабилизировалась давно (и обычно выглядит в виде библиотеки) и требует наличия тестов уже до текущей работы.
Практика как раз подтверждает обратное. Ведь существуют кучи фреймворков, может не все удачные, но они есть.
N>Твои примеры с бухгалтерией, физическим или графическим движком мне кажутся неадекватными. В них есть много тонкостей, которые могут на второй уже версии сильно меняться. Например, следующий DirectX, или требование пересчёта баланса впараллель на нескольких ядрах вместо текущего одного ядра могут переломать всю архитектуру слоя поддержки.
И? Одна тонкость не ставит под сомнение существование самих движков. И уж точно не говорит о том что их нельзя покрывать тестами.
N>Поэтому разумно просто отвести ресурсы на написание тестов, не конкретизируя, какие именно они должны быть. Где-то это юнит-тесты до каждой строчки кода, где-то — функциональный на группу классов. Осталось, чтобы руководитель согласился с критериями разумности.
Если рассматривать тесты только как пожиратель ресурсов, то их вообще писать не стоит. Я например прекрасно вижу преимущества unit-тестов в виде формально проверяемых спецификаций, а также как регрессионное тестирование. Если вы не видите, то вам они не нужны.
G>>>>Если же код вашего модуля относительно простой, и\или часто подвержен изменениям из-за изменяющихся требований, и\или опирается на некоторый внешний функционал, то лучше не делать unit-тесты, а integration-тесты. AVK>>>Интеграционные тесты не делаются для отдельного модуля. По определению. G>>Да, они делаются для модуля и некоторого подмножества связанных с ним. Обычно для самого модуля+вызываемых модулей из низлежащих слоев. N>Для меня это будет называться, скорее всего, функциональными тестами (а интеграционными будут проверки суммарной функциональности нескольких крупных блоков, не подчинённых друг другу, а работающими совместно).
Без разницы совершенно как оно будет называться.
Функциональные тесты — это тесты которые тестируют функционал, они бывают unit-тестами, которые тестируют один модуль в изоляции от других и интеграционными тестами, которые тестируют несколько модулей в связке.
Я обычно такой терминологией пользуюсь, но если вам удобно используйте любую другую.
Здравствуйте, gandjustas, Вы писали:
G>>>>>2)Unit-тесты абсолютно нет смысла писать после кода G>>>>>Если вы создавая приложение выделяете некоторую стабильную часть кода, которую редко затрагивают при изменении требований, и которая используется другими частями приложения, то её нужно покрывать unit-тестами. AVK>>>>Проблема только в том, что стабильность участков кода обычно определяется уже после их написания, что несколько противоречит твоему п.2. G>>>Тут именно и вопрос в том что стабильная часть кода должна быть определена заранее. Как некоторый framework, на основе которого будет строиться приложение.
N>>Здесь AndrewVK более прав: определение стабильной части кода заранее на практике скорее невозможно, а там, где возможно, она уже стабилизировалась давно (и обычно выглядит в виде библиотеки) и требует наличия тестов уже до текущей работы. G>Практика как раз подтверждает обратное. Ведь существуют кучи фреймворков, может не все удачные, но они есть.
Если ты пользуешься готовым фреймворком, то он отделён и развивается отдельно.
Если ты его создаёшь в процессе развития своего кода выделением из него, он вначале нестабилен.
N>>Твои примеры с бухгалтерией, физическим или графическим движком мне кажутся неадекватными. В них есть много тонкостей, которые могут на второй уже версии сильно меняться. Например, следующий DirectX, или требование пересчёта баланса впараллель на нескольких ядрах вместо текущего одного ядра могут переломать всю архитектуру слоя поддержки. G>И? Одна тонкость не ставит под сомнение существование самих движков.
Она подтверждает AVK: "стабильность участков кода обычно определяется уже после их написания".
G> И уж точно не говорит о том что их нельзя покрывать тестами.
Этого я и не говорю. Но уровень затрат ресурсов на покрытие тестами может и часто должен меняться в зависимости от того, насколько стабилизировались внешние условия и представления об основах реализации. Нет смысла плотно покрывать тестами то, что может завтра быть вынесено к лешим.
N>>Поэтому разумно просто отвести ресурсы на написание тестов, не конкретизируя, какие именно они должны быть. Где-то это юнит-тесты до каждой строчки кода, где-то — функциональный на группу классов. Осталось, чтобы руководитель согласился с критериями разумности. G>Если рассматривать тесты только как пожиратель ресурсов, то их вообще писать не стоит.
Вывод про "только" высосан из пальца.
G> Я например прекрасно вижу преимущества unit-тестов в виде формально проверяемых спецификаций, а также как регрессионное тестирование.
Регрессионное — да. Формально проверяемая спецификация — нет. Для этого нужна верификация, а не тестирование.
(В самом выдающемся случае можно уже из спецификаций породить тесты по методу quickcheck или PropEr — на случайных данных — и с какой-то вероятностью через некоторое слабопредсказуемое время найти контрпример.)
G> Если вы не видите, то вам они не нужны.
Грубый и бессмысленный наезд. Правильно было бы сказать, что они местами нужны больше, местами — меньше.
N>>>Твои примеры с бухгалтерией, физическим или графическим движком мне кажутся неадекватными. В них есть много тонкостей, которые могут на второй уже версии сильно меняться. Например, следующий DirectX, или требование пересчёта баланса впараллель на нескольких ядрах вместо текущего одного ядра могут переломать всю архитектуру слоя поддержки. G>>И? Одна тонкость не ставит под сомнение существование самих движков.
N>Она подтверждает AVK: "стабильность участков кода обычно определяется уже после их написания".
Что ты понимаешь под стабильностью? Я говорил про части кода, на которые не влияют или мало влияют изменения требований для приложения. Граф. движок очень хорошо подходит.
G>> И уж точно не говорит о том что их нельзя покрывать тестами. N>Этого я и не говорю. Но уровень затрат ресурсов на покрытие тестами может и часто должен меняться в зависимости от того, насколько стабилизировались
внешние условия и представления об основах реализации. Нет смысла плотно покрывать тестами то, что может завтра быть вынесено к лешим.
Именно, но ведь не любой код одинаково подвержен изменениям? Или ты вообще не можешь сказать насколько подвержена изменениям тот или иной модуль?
N>>>Поэтому разумно просто отвести ресурсы на написание тестов, не конкретизируя, какие именно они должны быть. Где-то это юнит-тесты до каждой строчки кода, где-то — функциональный на группу классов. Осталось, чтобы руководитель согласился с критериями разумности. G>>Если рассматривать тесты только как пожиратель ресурсов, то их вообще писать не стоит. N>Вывод про "только" высосан из пальца.
Ты про другое не пишешь.
G>> Я например прекрасно вижу преимущества unit-тестов в виде формально проверяемых спецификаций, а также как регрессионное тестирование. N>Регрессионное — да. Формально проверяемая спецификация — нет. Для этого нужна верификация, а не тестирование.
Если язык позволяет статически верифицировать то да, а иначе только тестами. Других способов доказать что код соотвествует спецификации просто нет.
G>> Если вы не видите, то вам они не нужны. N>Грубый и бессмысленный наезд. Правильно было бы сказать, что они местами нужны больше, местами — меньше.
Это уже вам решать.
Я привел доводы когда можно использовать unit-тесты. Доводы соответствуют опыту. Не согласны — никто же не заставляет вас использовать unit-тест.
Своих же доводов вы не приводите. Это отчасти и подталкивает к мысли что вы не видите ценности тестов.
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Про то что не ловят довольно спорно. Если код нормально покрыт юнит-тестами, то они вполне ловят "изменения логики для фикса моего бага": например, человеку написали баг, он нашел что в каком-то условии можно что-то убрать и убирает (не особо вникая в суть этого метода), баг перестает воспроизводится, но что-то может поломаться. Юнит-тесты заставляют еще раз задуматься об изменениях, помогают принимать более осознанные решения.
Кроме того юнит-тесты отвечают не на вопрос: Что сломалось?
а больше на вопрос: Где сломалось?
Самый большой плюс: Помогают писать более чистый код!
На метод из 100500 строк с кучей условий, юнит-тесты (особенно на моках) написать (покрыть на 100%) довольно сложно.
Здравствуйте, gandjustas, Вы писали:
N>>>>Твои примеры с бухгалтерией, физическим или графическим движком мне кажутся неадекватными. В них есть много тонкостей, которые могут на второй уже версии сильно меняться. Например, следующий DirectX, или требование пересчёта баланса впараллель на нескольких ядрах вместо текущего одного ядра могут переломать всю архитектуру слоя поддержки. G>>>И? Одна тонкость не ставит под сомнение существование самих движков. N>>Она подтверждает AVK: "стабильность участков кода обычно определяется уже после их написания". G>Что ты понимаешь под стабильностью? Я говорил про части кода, на которые не влияют или мало влияют изменения требований для приложения. Граф. движок очень хорошо подходит.
А я объяснил, как на самом деле они могут повлиять принципиально, хотя кажется, что не могут.:)
G>>> И уж точно не говорит о том что их нельзя покрывать тестами. N>>Этого я и не говорю. Но уровень затрат ресурсов на покрытие тестами может и часто должен меняться в зависимости от того, насколько стабилизировались внешние условия и представления об основах реализации. Нет смысла плотно покрывать тестами то, что может завтра быть вынесено к лешим. G>Именно, но ведь не любой код одинаково подвержен изменениям? Или ты вообще не можешь сказать насколько подвержена изменениям тот или иной модуль?
Заранее — нет. Требования постоянно уточняются и изменяются. Предсказать необходимое даже на год может быть достаточно тяжело, на большее время — практически невозможно. С каждой новой установкой мы получаем какие-то новые требования, под которые надо адаптироваться.
N>>>>Поэтому разумно просто отвести ресурсы на написание тестов, не конкретизируя, какие именно они должны быть. Где-то это юнит-тесты до каждой строчки кода, где-то — функциональный на группу классов. Осталось, чтобы руководитель согласился с критериями разумности. G>>>Если рассматривать тесты только как пожиратель ресурсов, то их вообще писать не стоит. N>>Вывод про "только" высосан из пальца. G>Ты про другое не пишешь.
Это как и чем, извините, надо читать, чтобы сделать такой вывод? Я постоянно прямым текстом говорю обратное.
G>>> Я например прекрасно вижу преимущества unit-тестов в виде формально проверяемых спецификаций, а также как регрессионное тестирование. N>>Регрессионное — да. Формально проверяемая спецификация — нет. Для этого нужна верификация, а не тестирование. G>Если язык позволяет статически верифицировать то да, а иначе только тестами. Других способов доказать что код соотвествует спецификации просто нет.
Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%. Доказательство даёт только верификация (в пределах применённой модели, которая сама может быть некорректной).
Тем не менее тесты нужны, потому что практически они действуют достаточно хорошо.
G>Я привел доводы когда можно использовать unit-тесты. Доводы соответствуют опыту. Не согласны — никто же не заставляет вас использовать unit-тест. G>Своих же доводов вы не приводите. Это отчасти и подталкивает к мысли что вы не видите ценности тестов.
Повторюсь, что я их постоянно привожу. Только особенности Вашего восприятия не позволяют это увидеть.
А "доводы, когда можно использовать unit-test" мне не нужны. Я их и сам напишу толпами.
Мне были бы полезны доводы, когда их реально _нужно_ использовать. Пока что с этим плохо, в основном есть две позиции — "они нахер не нужны" и "покрывайте тестами всё".
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, gandjustas, Вы писали:
G>>>> И уж точно не говорит о том что их нельзя покрывать тестами. N>>>Этого я и не говорю. Но уровень затрат ресурсов на покрытие тестами может и часто должен меняться в зависимости от того, насколько стабилизировались внешние условия и представления об основах реализации. Нет смысла плотно покрывать тестами то, что может завтра быть вынесено к лешим. G>>Именно, но ведь не любой код одинаково подвержен изменениям? Или ты вообще не можешь сказать насколько подвержена изменениям тот или иной модуль?
N>Заранее — нет. Требования постоянно уточняются и изменяются. Предсказать необходимое даже на год может быть достаточно тяжело, на большее время — практически невозможно. С каждой новой установкой мы получаем какие-то новые требования, под которые надо адаптироваться.
Так не бывает. В такой ситуации просто невозможно что-то спланировать. Если же рассмотреть рельный случай. Делается 3d игра, для нее пишется движок, она станет 2d? Нет. Алгоритм пересечения луча с плоскостью изменится? Тоже нет.
G>>>> Я например прекрасно вижу преимущества unit-тестов в виде формально проверяемых спецификаций, а также как регрессионное тестирование. N>>>Регрессионное — да. Формально проверяемая спецификация — нет. Для этого нужна верификация, а не тестирование. G>>Если язык позволяет статически верифицировать то да, а иначе только тестами. Других способов доказать что код соотвествует спецификации просто нет.
N>Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%.
Почему это? Если покрываются все варианты использования, то чем это не доказательство?
Whitebox тестирование может покрыть все варианты использования.
Или ты не писал никогда таких тестов?
N>Тем не менее тесты нужны, потому что практически они действуют достаточно хорошо.
Конкретизируй понятие "хорошо" что ли.
G>>Я привел доводы когда можно использовать unit-тесты. Доводы соответствуют опыту. Не согласны — никто же не заставляет вас использовать unit-тест. G>>Своих же доводов вы не приводите. Это отчасти и подталкивает к мысли что вы не видите ценности тестов.
N>Повторюсь, что я их постоянно привожу. Только особенности Вашего восприятия не позволяют это увидеть.
Ага, вроде "тесты это хорошо".
N>А "доводы, когда можно использовать unit-test" мне не нужны. Я их и сам напишу толпами. N>Мне были бы полезны доводы, когда их реально _нужно_ использовать. Пока что с этим плохо, в основном есть две позиции — "они нахер не нужны" и "покрывайте тестами всё".
Любая программа может быть написана без тестов, поэтому необходимости в них нет. Есть примеры когда разные типы тестов дают положительный эффект. Я их привожу, а ты с чем-то споришь.
Здравствуйте, gandjustas, Вы писали:
G>>>Именно, но ведь не любой код одинаково подвержен изменениям? Или ты вообще не можешь сказать насколько подвержена изменениям тот или иной модуль? N>>Заранее — нет. Требования постоянно уточняются и изменяются. Предсказать необходимое даже на год может быть достаточно тяжело, на большее время — практически невозможно. С каждой новой установкой мы получаем какие-то новые требования, под которые надо адаптироваться. G>Так не бывает. В такой ситуации просто невозможно что-то спланировать. Если же рассмотреть рельный случай. Делается 3d игра, для нее пишется движок, она станет 2d? Нет. Алгоритм пересечения луча с плоскостью изменится? Тоже нет.
Алгоритм — нет. А вот оптимальная реализация — да, может сильно измениться в зависимости от специфики запросов и обстановки.
Насчёт "невозможно что-то спланировать" ты сильно неправ. Возможно. Но планировать надо иначе — умея уточнять обстановку и адаптироваться под неё на ходу. Такое себе agile, только на следующем вверх уровне. И таки да, быть готовым морально и практически к тому, что что-то придётся отменять ещё не доделанное, с сожалением, и переходить к другому.
В быстро развивающемся софте, как у нас, может устаревать 20-30% кода в год. И это не самый суровый темп.
N>>Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%. G>Почему это? Если покрываются все варианты использования, то чем это не доказательство?
Что ты называешь вариантами использования?
G>Whitebox тестирование может покрыть все варианты использования.
Сунул строку "whitebox тестирование" в яндекс. Получил рассказ о том, как наши космические корабли бороздят просторы Большого театра инструменты вроде PEX берут функцию и находят характерные входные данные для проверки. Ты это имел в виду? Или таки вариант проверить все возможные значения множества входных данных? В последнем случае, сколько триллионов лет потребуется тебе для завершения тестирования, например, функции подсчёта длины utf-8 строки, в кодовых пунктах?
А то же самое, но в символах?
G>Или ты не писал никогда таких тестов?
Каких "таких"? Whitebox? Писал. По крайней мере то, что я знаю под этим термином. Но не думаю, что это само по себе позволит накормить 5 хлебами 5K человек всунуть работу до конца Вселенной в сроки в сутках.
N>>Тем не менее тесты нужны, потому что практически они действуют достаточно хорошо. G>Конкретизируй понятие "хорошо" что ли.
Я не пытаюсь совершить невозможное.
G>>>Я привел доводы когда можно использовать unit-тесты. Доводы соответствуют опыту. Не согласны — никто же не заставляет вас использовать unit-тест. G>>>Своих же доводов вы не приводите. Это отчасти и подталкивает к мысли что вы не видите ценности тестов. N>>Повторюсь, что я их постоянно привожу. Только особенности Вашего восприятия не позволяют это увидеть. G>Ага, вроде "тесты это хорошо".
И что в этом неправильного? Да, это общее руководство. Да, я мог бы сказать, что для наших условий юнит-тест для нетривиальной функции экономит усилия в 10-20-100 раз (в зависимости от местности) по сравнению с выявлением проблемы уже на функциональном тесте приложения и поиском точки проблемы доступными средствами (только логами, поскольку никакой отладчик наши задачи не вытянет), а комплект функциональных тестов хотя бы на базовые сценарии работы приложения экономит ну просто дофига хотя бы на том, что установленная клиенту система в основном работает, а не падает. Но какое другим дело до нашей весьма тяжёлой специфики? У них своя есть, не менее странная.
N>>А "доводы, когда можно использовать unit-test" мне не нужны. Я их и сам напишу толпами. N>>Мне были бы полезны доводы, когда их реально _нужно_ использовать. Пока что с этим плохо, в основном есть две позиции — "они нахер не нужны" и "покрывайте тестами всё".
G>Любая программа может быть написана без тестов, поэтому необходимости в них нет. Есть примеры когда разные типы тестов дают положительный эффект. Я их привожу, а ты с чем-то споришь.
То есть ты даже не понял, с чем именно я спорю? Тогда, может, следовало начать с более тщательного анализа моих сообщений, прежде чем возражать на них? ;)
чуть переставлю секции.
AVK>>>Википедия — достаточно массово? N>>Нет. AVK>:)))
Ничего смешного. Во-первых, она одна. Во-вторых, в ней тоже есть противоречия. Например, в русской "тестирование белого ящика" в основной статье по тестированию предполагает возможность доступа к коду, но не требует какой-то определённой методики, а отдельная статья по тому же методу — предполагает автоматизированное построение теста по коду с покрытием рассмотренных там условий. Это уже заметно разные подходы. (См. ниже про ещё противоречия.)
У нас же вообще используется ещё одно понимание такого деления — когда blackbox это использование только документированных интерфейсов, а whitebox — с выставления доступа ко всем внутренностям (как если бы все функции экспортировались, а все члены классов были бы public); в частности, такой whitebox нужен для проверки внутренних, но сложных функций, и именно такое деление мне кажется наиболее полезным, и оно формально не совпадает ни с одним из википедийных.
AVK>>>Так что TDD все таки функциональные тесты предполагает в обязательном порядке. А unit уже по желанию. N>>Это с чего такой вывод? Во-первых, там нет ни слова про functional, integration или что-то подобное, там только test case. AVK>С того, что "test case that defines a desired improvement or new function" это функциональный тест. По определению: AVK>
AVK>Functional testing refers to activities that verify a specific action or function of the code. These are usually found in the code requirements documentation, although some development methodologies work from use cases or user stories. Functional tests tend to answer the question of "can the user do this" or "does this particular feature work."
Такое твоё понимание бессмысленно, потому что по нему любой тест, который не является анализом кода без его запуска, является функциональным. Запустили — значит, функциональный тест. Но тогда в чём отличие от других тестов? Я точно так же мог бы сказать не "функциональный тест", а "хрень", потому что функциональный тест — частный случай хрени.
А ответ тут в том, что ты в своём художественном квотинге по тексту этой статьи "упустил" то, что там функциональное тестирование противопоставляется тестированию других характеристик, таких, как нагрузочная способность или защищённость. А в этом случае нет никакого противоречия между functional и unit testing: второе всего лишь частный случай первого, сведённый до одного кусочка кода. Из той же статьи:
Unit testing refers to tests that verify the functionality of a specific section of code, usually at the function level.
Английским по фоновому — "verify the functionality", значит, это функциональный тест.
Возвращаясь к TDD, его особенность — тестирование каждого нового куска методом предварительного построения теста, доказывающего неработу пустого места;) при этом никак не ограничивается размер этого куска. Специально взял ту же википедию:
first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards.
"New function" является вполне допустимым объёмом добавления. И в чём противоречие с unit testing? Идём ниже, в той же странице:
Test-driven development requires developers to create automated unit tests that define code requirements (immediately) before writing the code itself.
Тут уже чётко написано про "unit" tests. А не "functional".
В общем, пожелание: если уж ссылаешься на источник, постарайся хоть ему не противоречить:) иначе нелепо выглядишь. И брать для этого источники, которые хотя бы внутренне согласованы, а не как википедия.
AVK>Тестирование на живых процессах, когда запускается приложение целиком, это интеграционное тестирование.
Это для тебя есть понятие "приложение целиком". У меня application (приложение) — это всего лишь компонента рабочей среды исполнения, характерная тем, что имеет логические действия запуска и останова (а также, впадая в детали, у неё может быть failover и takeover между местами исполнения, причём на разных средствах, и с передачей состояния или без передачи). А интеграционное тестирование — это уже для комплекса из нескольких приложений.
AVK> Функциональное точно так же тестирует в основном отдельные модули. Разница с unit в том, что последнее тестирует все аспекты контракта, а функциональное проверяет работоспособность модуля на предполагаемых юзкейсах. Т.е. unit-тестирование проверяет соответствие контракта спецификации, а функциональное — соответствие функционала входному ТЗ.
В вашей фирме так понимают эти термины? Охотно верю.
N>>Функциональные тесты являются основным проверяемым фактором для QA — наш QA про юнит-тесты ничего не знает, это дело программистов и меня как начальника группы программистов.
AVK>Это вопрос организационный, к разделению на юнит и функциональное это никакого отношения не имеет. Функциональные тесты очень часто пишутся именно программистами.
Пишутся — да. А проверяются — уже по-разному. QA у нас юнит-тестирование не запускает.
N>>Некорректный логический переход. ТЗ на функцию, да, описывает её функционал, но понятие функциональных тестов подразумевает проверку логически завершённого модуля AVK>Ничего подобного оно не подразумевает. См. определение.
Которое из противоречащих? ;)
N>>Википедия, BTW, далеко не единственный источник. AVK>О да, знакомо. Если что то не стыкуется — виновата википедия. AVK>Не, она, конечно, далеко не единственный и даже не самый авторитетный источник, но если выбирать между ней и твоими голословными заявлениями, то я предпочту первое.
У тебя результат не менее голословный получился — на самопротиворечивом источнике.
N>> В описании этих методик я больше опирался на статьи с хабра AVK>О! Зашибись источник. Все с вами ясно. N>>, чем на википедию, и считаю это в данном случае более разумным. AVK>Считай.
Здравствуйте, netch80, Вы писали:
N>Например, в русской
Я русскую почти никогда и не читаю. И цитаты приводил из английской. Но вообще, переход спора на обсуждение качества википедии уже о многом говорит.
AVK>>С того, что "test case that defines a desired improvement or new function" это функциональный тест. По определению: AVK>>
AVK>>Functional testing refers to activities that verify a specific action or function of the code. These are usually found in the code requirements documentation, although some development methodologies work from use cases or user stories. Functional tests tend to answer the question of "can the user do this" or "does this particular feature work."
N>Такое твоё понимание бессмысленно
Это не мое понимание, это цитата из википедии. Ну да я помню, она у тебя рожей не вышла.
N>А ответ тут в том, что ты в своём художественном квотинге по тексту этой статьи "упустил" то, что там функциональное тестирование противопоставляется тестированию других характеристик, таких, как нагрузочная способность или защищённость. А в этом случае нет никакого противоречия между functional и unit testing
А кто говорил про противоречия? Да, часть функциональных тестов вполне может прокатить и под юнит. Разница в подходе. При функциональном тестировании мы, фактически, формализуем юзкейсы в виде набора тестов. А при юнит — фиксируем спецификации конкретных модулей в конкретном дизайне.
N>Возвращаясь к TDD, его особенность — тестирование каждого нового куска методом предварительного построения теста
Его особенность — это не тестирование куска, пусть и предварительное. Его особенность — предварительная формализация ТЗ путем написания тестов. И дальнейшее описание дизайна — тоже при помощи тестов. Да, прижелании TDD можно использовать вплоть до уровня классов, и тогда функциональные тесты на этом уровне вполне прокатят и за юнит (хотя будут, по меркам юнит-тестирования, сильно неполными). Ну так и что?
AVK>>Тестирование на живых процессах, когда запускается приложение целиком, это интеграционное тестирование. N>Это для тебя есть понятие "приложение целиком". У меня application (приложение) — это всего лишь компонента рабочей среды исполнения, характерная тем, что имеет логические действия запуска и останова (а также, впадая в детали, у неё может быть failover и takeover между местами исполнения, причём на разных средствах, и с передачей состояния или без передачи). А интеграционное тестирование — это уже для комплекса из нескольких приложений.
Куча слов, а мысли я уловить не смог.
... << RSDN@Home 1.2.0 alpha 5 rev. 1530 on Windows 7 6.1.7601.65536>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, netch80, Вы писали: N>>Например, в русской AVK>Я русскую почти никогда и не читаю. И цитаты приводил из английской. Но вообще, переход спора на обсуждение качества википедии уже о многом говорит.
Там и по английской и расхождения между отдельными статьями, и совсем не то, что ты утверждаешь.
AVK>>>С того, что "test case that defines a desired improvement or new function" это функциональный тест. По определению: AVK>>>
AVK>>>Functional testing refers to activities that verify a specific action or function of the code. These are usually found in the code requirements documentation, although some development methodologies work from use cases or user stories. Functional tests tend to answer the question of "can the user do this" or "does this particular feature work."
N>>Такое твоё понимание бессмысленно AVK>Это не мое понимание, это цитата из википедии. Ну да я помню, она у тебя рожей не вышла.
Не статья "рожей не вышла", а твоя её "интерпретация" с устранением принципиальной мысли данного раздела статьи и подменой тезиса. Сразу за процитированным тобой абзацем идёт текст:
Non-functional testing refers to aspects of the software that may not be related to a specific function or user action, such as scalability or other performance, behavior under certain constraints, or security. Non-functional requirements tend to be those that reflect the quality of the product, particularly in the context of the suitability perspective of its users.
Отсюда чётко ясно, что в процитированном тобой нет никакого противопоставления одних видов функционального тестирования другим, но есть противопоставление другим видам, как нагрузочное.
А так как unit testing, согласно тому же источнику, проверяет, то ли делает функция, то это тоже функциональное.
N>>А ответ тут в том, что ты в своём художественном квотинге по тексту этой статьи "упустил" то, что там функциональное тестирование противопоставляется тестированию других характеристик, таких, как нагрузочная способность или защищённость. А в этом случае нет никакого противоречия между functional
и unit testing
AVK>А кто говорил про противоречия? Да, часть функциональных тестов вполне может прокатить и под юнит. Разница в подходе. При функциональном тестировании мы, фактически, формализуем юзкейсы в виде набора тестов. А при юнит — фиксируем спецификации конкретных модулей в конкретном дизайне.
Тогда покажи мне, как по википедии (ты ведь этот источник мне показываешь в качестве своей основной опоры?) в юнит-тестах ты _фиксируешь_ _спецификации_. И как пример кода из статьи Unit testing со строками вида
assert(adder.add(2, 2) == 4);
может "фиксировать спецификацию". Покажи, а я посмотрю:)
Нельзя подходить к источникам по типу "здесь читаю, а здесь рыбу заворачиваю". Если ты мне предъявляешь википедию как надёжный источник — соглашайся с ней во всём, а не выборочно. Или признай, что она тоже не более чем подсказка, но тогда я получу полное право использовать и другие источники, и часто как более авторитетные.
N>>Возвращаясь к TDD, его особенность — тестирование каждого нового куска методом предварительного построения теста AVK>Его особенность — это не тестирование куска, пусть и предварительное. Его особенность — предварительная формализация ТЗ путем написания тестов.
В том и дело, что это однобокое рассмотрение TDD. Можно формализовать ТЗ и больше ничего не делать (тесты не пускать, а код пусть живёт своей жизнью). Кроме собственно формализации ТЗ, TDD выполняет ещё две роли: проверку соответствия этому ТЗ для автора кода и проверку того же для его начальства. Если тесты пишутся вслед, крайне велик соблазн их просто не делать или сделать когда будет время после гонки за функционалом (а этого времени таки не будет). Предварительное написание тестов является жёстким методом гарантировать проверку хотя бы этих тестов соответствия ТЗ. Да, для этого необходимым условием является предварительная формализация ТЗ, о чём ты собственно и пишешь. Но по сути этот метод — не технический, а административный: он даёт какую-то гарантию менеджменту, что код реально есть, потому что не протестированный код — его как бы и нет. И первый недостаток его — опять же в административной плоскости: нет реального критерия, что именно проверять.
AVK>>>Тестирование на живых процессах, когда запускается приложение целиком, это интеграционное тестирование. N>>Это для тебя есть понятие "приложение целиком". У меня application (приложение) — это всего лишь компонента рабочей среды исполнения, характерная тем, что имеет логические действия запуска и останова (а также, впадая в детали, у неё может быть failover и takeover между местами исполнения, причём на разных средствах, и с передачей состояния или без передачи). А интеграционное тестирование — это уже для комплекса из нескольких приложений.
AVK>Куча слов, а мысли я уловить не смог.
Тогда начни с одной простой фразы: приложение — это не то, что тебе кажется. :)
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, gandjustas, Вы писали:
G>>>>Именно, но ведь не любой код одинаково подвержен изменениям? Или ты вообще не можешь сказать насколько подвержена изменениям тот или иной модуль? N>>>Заранее — нет. Требования постоянно уточняются и изменяются. Предсказать необходимое даже на год может быть достаточно тяжело, на большее время — практически невозможно. С каждой новой установкой мы получаем какие-то новые требования, под которые надо адаптироваться. G>>Так не бывает. В такой ситуации просто невозможно что-то спланировать. Если же рассмотреть рельный случай. Делается 3d игра, для нее пишется движок, она станет 2d? Нет. Алгоритм пересечения луча с плоскостью изменится? Тоже нет.
N>Алгоритм — нет. А вот оптимальная реализация — да, может сильно измениться в зависимости от специфики запросов и обстановки.
Точно, поэтому и нужны unit-тесты чтобы проверить что новый и старый алгоритм эквивалентны
N>Насчёт "невозможно что-то спланировать" ты сильно неправ. Возможно. Но планировать надо иначе — умея уточнять обстановку и адаптироваться под неё на ходу. Такое себе agile, только на следующем вверх уровне. И таки да, быть готовым морально и практически к тому, что что-то придётся отменять ещё не доделанное, с сожалением, и переходить к другому.
А ты не в кусре что agile не работает\плохо работает на fixed cost? тебя много не fixed cost проектов было?
N>В быстро развивающемся софте, как у нас, может устаревать 20-30% кода в год. И это не самый суровый темп.
Точно и остается 70%-80% кода в год, часть из которого не меняется на протяжении многих лет. Вернее меняется код, не меняются контракты.
Ты только что показал существование такого, который редко меняется в плане контрактов и для которого имеет смысл писать тесты.
N>>>Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%. G>>Почему это? Если покрываются все варианты использования, то чем это не доказательство? N>Что ты называешь вариантами использования?
Это значит все возможные наборы параметров для которых имеются разные пути исполнения.
G>>Whitebox тестирование может покрыть все варианты использования.
N>Сунул строку "whitebox тестирование" в яндекс. Получил рассказ о том, как наши космические корабли бороздят просторы Большого театра инструменты вроде PEX берут функцию и находят характерные входные данные для проверки. Ты это имел в виду? Или таки вариант проверить все возможные значения множества входных данных? В последнем случае, сколько триллионов лет потребуется тебе для завершения тестирования, например, функции подсчёта длины utf-8 строки, в кодовых пунктах?
Не надо проверять все параметры. надо написать по одному тесту для каждого варианта использования. Их всегда конечное количество так как путей исполнения кода конечное количество. Как будешь вычислять эти самые варианты — твое дело, хоть автоматически с помощью pex, хоть анализируя код — без разницы. Вообще идеальный вариант — заранее продумывать как будет работать тот или иной метод класса и писать тесты. В любом случае вариантов использование будет очень ограниченное количество.
После того как написаны тесты с использованием whitebox методики можно к ним относиться как к backbox. То есть мы "забываем" что тесты проверяют какие-то варианты и нам главное чтобы они выполнялись. Потом мы можем изменять реализацию. оптимизировать как нам вздумается.
N>>>Тем не менее тесты нужны, потому что практически они действуют достаточно хорошо. G>>Конкретизируй понятие "хорошо" что ли. N>Я не пытаюсь совершить невозможное.
Во фразе выше есть слово "хорошо", что ты под ним понимаешь?
G>>>>Я привел доводы когда можно использовать unit-тесты. Доводы соответствуют опыту. Не согласны — никто же не заставляет вас использовать unit-тест. G>>>>Своих же доводов вы не приводите. Это отчасти и подталкивает к мысли что вы не видите ценности тестов. N>>>Повторюсь, что я их постоянно привожу. Только особенности Вашего восприятия не позволяют это увидеть. G>>Ага, вроде "тесты это хорошо".
N>И что в этом неправильного? Да, это общее руководство. Да, я мог бы сказать, что для наших условий юнит-тест для нетривиальной функции экономит усилия в 10-20-100 раз (в зависимости от местности) по сравнению с выявлением проблемы уже на функциональном тесте приложения и поиском точки проблемы доступными средствами (только логами, поскольку никакой отладчик наши задачи не вытянет), а комплект функциональных тестов хотя бы на базовые сценарии работы приложения экономит ну просто дофига хотя бы на том, что установленная клиенту система в основном работает, а не падает. Но какое другим дело до нашей весьма тяжёлой специфики? У них своя есть, не менее странная.
Ну вот, уже конкретика пошла, это уже лучше чем просто "хорошо".
Вообще я заметил склонность многих программистов считать свои проекты\задачи пипец какими уникальными, а по сути сколько не смотрел проекты похожи один на другой даже из разных областей. И ошибки в них одинаковые.
Здравствуйте, Aikin, Вы писали:
AVK>>>> А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне. A>>>Мы все еще про юнит-тесты говорим? Тогда о каких пачках идет речь? AVK>>Об обычных. Глубокий рефакторинг ломает конктракты — ломаются все связанные с этим тесты. A>Любой рефакторинг идет итеративно, небольшими шажками. Тесты отваливаются постепенно. В конце каждой итерации зеленыее тесты поддтверждат корректность текущего шага. A>Я вообще не представляю как можно делать рефакторинг в стиле "все похерим, а потом разберемся". A>При "очень глубоком рефакторинге" классы вместе с тестами проще выкинуть, чем изменять.
A>И вообще, как часто системе необходим "глубоком рефакторинг" (и что это за зверь-то?)
В сильно запущеном коде рефакторить нужно вообще постоянно, по 50 файлов например изменить что бы кнопку перенести с одной формы на другую
Здравствуйте, gandjustas, Вы писали:
N>>Алгоритм — нет. А вот оптимальная реализация — да, может сильно измениться в зависимости от специфики запросов и обстановки. G>Точно, поэтому и нужны unit-тесты чтобы проверить что новый и старый алгоритм эквивалентны ;)
Против этого я и не возражал.:) Вопрос в том, насколько они unit.
N>>Насчёт "невозможно что-то спланировать" ты сильно неправ. Возможно. Но планировать надо иначе — умея уточнять обстановку и адаптироваться под неё на ходу. Такое себе agile, только на следующем вверх уровне. И таки да, быть готовым морально и практически к тому, что что-то придётся отменять ещё не доделанное, с сожалением, и переходить к другому. G>А ты не в кусре что agile не работает\плохо работает на fixed cost? тебя много не fixed cost проектов было?
Я в курсе. Но я предполагаю, что если заказчик меняет требования посреди реализации, то он должен за это заплатить.
На второй вопрос — я вообще не помню у себя fixed cost проектов за последние лет 15. Все — долгоживущие, рассчитанные на продолжение жизни софтины до тех пор, пока в ней есть хоть какой-то смысл, или прототипы, которые не вышли дальше этого этапа.
В случае жёсткого fixed cost я бы, конечно, любые предложения чего-то подправить сначала бы встречал правым нижним в бубен, а затем начинал бы слушать, что заказчик хочет. (Утрирую, конечно;))
N>>В быстро развивающемся софте, как у нас, может устаревать 20-30% кода в год. И это не самый суровый темп. G>Точно и остается 70%-80% кода в год, часть из которого не меняется на протяжении многих лет. Вернее меняется код, не меняются контракты. G>Ты только что показал существование такого, который редко меняется в плане контрактов и для которого имеет смысл писать тесты.
Категорически согласен. Так вот, и продолжая эту мысль: у нас есть некоторый ресурсный фонд на поддержание своего продукта, а не только на новое (если на поддержание дают 0%, надо немедленно увольняться). В момент введения новой функциональности она протестирована едва-едва, только в самых ключевых точках и на уровне самой верхней целевой функциональности, но как только она выжила — часть этого фонда поддержания должна пойти на усиление тестами того, что уже выжило. Ещё выжило — ещё усилить. И так далее.
N>>>>Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%. G>>>Почему это? Если покрываются все варианты использования, то чем это не доказательство? N>>Что ты называешь вариантами использования? G>Это значит все возможные наборы параметров для которых имеются разные пути исполнения.
Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно.
G>Не надо проверять все параметры. надо написать по одному тесту для каждого варианта использования. Их всегда конечное количество так как путей исполнения кода конечное количество. Как будешь вычислять эти самые варианты — твое дело, хоть автоматически с помощью pex, хоть анализируя код — без разницы. Вообще идеальный вариант — заранее продумывать как будет работать тот или иной метод класса и писать тесты. В любом случае вариантов использование будет очень ограниченное количество.
Под такое ещё надо адаптировать код. Далеко не всегда возможно, хотя, надо заметить, может быть полезно. Для того же примера с подсчётом длины строки, например, разделить код на функцию сдвига на один пункт и код подсчёта пунктов может быть полезно для ловли, например, ситуации, если промежуточная длина хранится в 8 битах (злобный пример того, как можно не заметить фигню на коротких тестах). Но это уже придётся mock'и делать.
А без адаптации, по крайней мере для моей обстановки, никак не получается "очень ограниченное количество" ситуаций. Поэтому для меня whitebox testing имеет совсем другой смысл (см. предыдущие письма).
N>>И что в этом неправильного? Да, это общее руководство. Да, я мог бы сказать, что для наших условий юнит-тест для нетривиальной функции экономит усилия в 10-20-100 раз (в зависимости от местности) по сравнению с выявлением проблемы уже на функциональном тесте приложения и поиском точки проблемы доступными средствами (только логами, поскольку никакой отладчик наши задачи не вытянет), а комплект функциональных тестов хотя бы на базовые сценарии работы приложения экономит ну просто дофига хотя бы на том, что установленная клиенту система в основном работает, а не падает. Но какое другим дело до нашей весьма тяжёлой специфики? У них своя есть, не менее странная. G>Ну вот, уже конкретика пошла, это уже лучше чем просто "хорошо". G>Вообще я заметил склонность многих программистов считать свои проекты\задачи пипец какими уникальными, а по сути сколько не смотрел проекты похожи один на другой даже из разных областей. И ошибки в них одинаковые.
Ты путаешь совершенно разные вещи. Ошибки часто одинаковые, это да. Специфика не уникальна, но она колоссально разная. Я не могу найти сейчас человека здесь на форуме, который заметен и который занимается хотя бы примерно теми же задачами, что я. Если есть, то они не светятся. В то же время я знаю, что в мире ещё около 5 групп идёт схожими путями. Но они не здесь. Поэтому я действительно склонен считать, что в пределах RSDN мои задачи уникальны и специфика их — тоже. Возражать разрешаю только на собственных примерах;)
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, gandjustas, Вы писали:
N>>>Алгоритм — нет. А вот оптимальная реализация — да, может сильно измениться в зависимости от специфики запросов и обстановки. G>>Точно, поэтому и нужны unit-тесты чтобы проверить что новый и старый алгоритм эквивалентны
N>Против этого я и не возражал. Вопрос в том, насколько они unit.
А что им мешает быть unit-тестами?
N>>>>>Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%. G>>>>Почему это? Если покрываются все варианты использования, то чем это не доказательство? N>>>Что ты называешь вариантами использования? G>>Это значит все возможные наборы параметров для которых имеются разные пути исполнения.
N>Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно.
Вообще-то конечен. Но все равно я не предлагаю весь код покрывать, я предлагаю покрывать ту функциональность конракты которой будут меняться редко или не меняться вообще. И как ты понимаешь на fixed cost проектах (каковых большинство) надо заранее планировать объем работ. Из этого объема легко вычленить части, соотвествующие тому что я написал выше.
G>>Не надо проверять все параметры. надо написать по одному тесту для каждого варианта использования. Их всегда конечное количество так как путей исполнения кода конечное количество. Как будешь вычислять эти самые варианты — твое дело, хоть автоматически с помощью pex, хоть анализируя код — без разницы. Вообще идеальный вариант — заранее продумывать как будет работать тот или иной метод класса и писать тесты. В любом случае вариантов использование будет очень ограниченное количество.
N>Под такое ещё надо адаптировать код. Далеко не всегда возможно, хотя, надо заметить, может быть полезно. Для того же примера с подсчётом длины строки, например, разделить код на функцию сдвига на один пункт и код подсчёта пунктов может быть полезно для ловли, например, ситуации, если промежуточная длина хранится в 8 битах (злобный пример того, как можно не заметить фигню на коротких тестах). Но это уже придётся mock'и делать.
Если писать тесты до кода, то он как-то сам адаптируется
Пишешь простой тесты, пишешь для него простой код, простой код не удовлетворяет всем требованиям, пишешь еще один простой тест, правишь под два теста код, пишешь третий тест, начинаешь писать код, понимаешь что друге тесты надо править чтобы не падали, сразу возникает желание вынести код в другой класс\функцию\еще что-нить, а в данном классе оставить mock.
Для TDD (test-first) это естественный процесс, для test-after — нет. Поэтому unit-тесты и рекомендуется для test-first.
N>А без адаптации, по крайней мере для моей обстановки, никак не получается "очень ограниченное количество" ситуаций. Поэтому для меня whitebox testing имеет совсем другой смысл (см. предыдущие письма).
То есть изначально написан плохо тестируемый код. Но эту ситуацию ты экстраполируешь на другие случаи, что далеко неверно.
N>Ты путаешь совершенно разные вещи. Ошибки часто одинаковые, это да. Специфика не уникальна, но она колоссально разная. Я не могу найти сейчас человека здесь на форуме, который заметен и который занимается хотя бы примерно теми же задачами, что я. Если есть, то они не светятся. В то же время я знаю, что в мире ещё около 5 групп идёт схожими путями. Но они не здесь. Поэтому я действительно склонен считать, что в пределах RSDN мои задачи уникальны и специфика их — тоже. Возражать разрешаю только на собственных примерах
Я понятия не имею чем ты занимаешь, но если завесу тайны уберешь, то найдется много людей кто занимается тем-же или похожим.
Посмотрев пару сотен сообщений в твоем профиле видно что большую часть ты тут пишешь в КСВ и о жизни, инода отвечая на темы о сетевых протоколах и С++.
Поэтому то чем ты занимаешься связано с сетями, точно не web, скорее всего linux\C++. Возможно что-то в высокой вычислительной нагрузкой типа аудио\видео конференц связи или обработкой видеопотоков от камер.
Учитывая что основной язык для тебя — C++, то там unit-тесты с трудом возможны, ибо компиляция долгая и нормальных mock-фреймворков нету.
Здравствуйте, gandjustas, Вы писали:
N>>>>Алгоритм — нет. А вот оптимальная реализация — да, может сильно измениться в зависимости от специфики запросов и обстановки. G>>>Точно, поэтому и нужны unit-тесты чтобы проверить что новый и старый алгоритм эквивалентны ;) N>>Против этого я и не возражал.:) Вопрос в том, насколько они unit. G>А что им мешает быть unit-тестами?
Зависит от глубины переделки. Может оказаться, что неизменна и покрыто неизменившимися тестами только самая внешняя функциональность, которая не может быть сведена к простому "выстрелил вызовом функции — получил ответ — проверил", а требует долгой подготовки среды. Для меня это всё-таки не unit тест. Unit — это когда максимум подготовки среды это расстановка заглушек вместо рабочего кода. Да, это деление не академично, но мне оно полезнее.
N>>>>>>Ошибка в слове "доказать". Никакими тестами ты не можешь это _доказать_, можешь лишь обещать какую-то вероятность или меру корректности. Она может быть и 99.999%, но не 100%. G>>>>>Почему это? Если покрываются все варианты использования, то чем это не доказательство? N>>>>Что ты называешь вариантами использования? G>>>Это значит все возможные наборы параметров для которых имеются разные пути исполнения. N>>Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно. G>Вообще-то конечен.
Ну числа типа 2**2**32 всё равно за пределами представления даже в BER:), поэтому я позволил себе "округлить" их до бесконечности.
G> Но все равно я не предлагаю весь код покрывать, я предлагаю покрывать ту функциональность конракты которой будут меняться редко или не меняться вообще. И как ты понимаешь на fixed cost проектах (каковых большинство) надо заранее планировать объем работ. Из этого объема легко вычленить части, соотвествующие тому что я написал выше.
Я ни разу не планировал сам fixed-cost проекты, поэтому тут ничего не скажу. Приму просто как должное.
G>>>Не надо проверять все параметры. надо написать по одному тесту для каждого варианта использования. Их всегда конечное количество так как путей исполнения кода конечное количество. Как будешь вычислять эти самые варианты — твое дело, хоть автоматически с помощью pex, хоть анализируя код — без разницы. Вообще идеальный вариант — заранее продумывать как будет работать тот или иной метод класса и писать тесты. В любом случае вариантов использование будет очень ограниченное количество.
N>>Под такое ещё надо адаптировать код. Далеко не всегда возможно, хотя, надо заметить, может быть полезно. Для того же примера с подсчётом длины строки, например, разделить код на функцию сдвига на один пункт и код подсчёта пунктов может быть полезно для ловли, например, ситуации, если промежуточная длина хранится в 8 битах (злобный пример того, как можно не заметить фигню на коротких тестах). Но это уже придётся mock'и делать.
G>Если писать тесты до кода, то он как-то сам адаптируется ;)
Писать тесты до кода означает или гарантированно окончательный дизайн, во что я не верю, или тупую наивность. По крайней мере я других вариантов пока не видел. Если есть где-то место, где это проходит, то там кому-то сильно повезло.
Я периодически использовал этот подход — тесты до кода — как стимулятор собственной работы против собственной же лени, то есть проблема была чисто организационная. Но я никогда не предполагал его как средство собственно дизайна. На это годится тестируемость в принципе, а не test-first.
G>Пишешь простой тесты, пишешь для него простой код, простой код не удовлетворяет всем требованиям, пишешь еще один простой тест, правишь под два теста код, пишешь третий тест, начинаешь писать код, понимаешь что друге тесты надо править чтобы не падали, сразу возникает желание вынести код в другой класс\функцию\еще что-нить, а в данном классе оставить mock. G>Для TDD (test-first) это естественный процесс, для test-after — нет. Поэтому unit-тесты и рекомендуется для test-first.
Не так. Оцениваешь функциональность. Пишешь прототип, или даже просто схему на бумаге, что должно делаться и как. Смотришь на него, проводишь фактически design review (хоть в одиночку, но можно и с коллегами), оцениваешь, что и как реализовывать. На этом этапе могут выделяться чёткие подблоки, которые пригодны к тестированию отдельно от остального. Но ещё не под тест, потому что могут интерфейсы и контракты поменяться уже в процессе реализации, когда будет осознан ещё один пласт специфики. Потом всё это приблизительно написано, разделение на составные части, интерфейсы, контракты стабилизировались, теперь нетривиальные внутренние части покрываются базовыми тестами, подтверждающими их работоспособность. В основном это юнит-тесты, с минимальной настройкой среды проверки или вообще без неё, на уровне отдельных функций. Далее рассчитываешь и делаешь функциональные тесты, под базовые юзкейсы компонент, вычищаешь их от багов. В моём случае обычно минимальная единица, обладающая собственным поведением, называется приложением, и делать функциональные тесты для более мелких частей обычно нереально (хотя бывают и такие сущности). На следующем уровне тестируются связки из таких приложений, образующие функциональность части полной системы, например, тракт вокруг конкретной шины от первичных генераторов событий до финальных накопителей; назовём это интеграционными тестами. Только после этого можно говорить о завершении разработки конкретной подсистемы или даже приложения — когда оно показывает корректное прохождение интеграционных тестов. Далее идут общесистемные тесты, это уже делается людьми в QA отделе. Процесс такого развития никак не соответствует продвижению сверху вниз с твёрдой уверенностью в функционале конкретного уровня, а вместо этого можно говорить только о приблизительной чёткости понимания, которая будет доведена до полной уже после пробы на прототипе. Вот-с, где-то так.
N>>А без адаптации, по крайней мере для моей обстановки, никак не получается "очень ограниченное количество" ситуаций. Поэтому для меня whitebox testing имеет совсем другой смысл (см. предыдущие письма). G>То есть изначально написан плохо тестируемый код. Но эту ситуацию ты экстраполируешь на другие случаи, что далеко неверно.
Я продолжу эксплуатировать тот же пример — подсчёт codepoints. Ты действительно считаешь, что если функцию такого рода нельзя разделить на части, проверяемые по методу такого whitebox и подменяемые mock'ами, то это "изначально плохо тестируемый код"? Ответь, пожалуйста, конкретно, а не в сторону. Мне таки очень интересно. Пример простой, но полезный.
N>>Ты путаешь совершенно разные вещи. Ошибки часто одинаковые, это да. Специфика не уникальна, но она колоссально разная. Я не могу найти сейчас человека здесь на форуме, который заметен и который занимается хотя бы примерно теми же задачами, что я. Если есть, то они не светятся. В то же время я знаю, что в мире ещё около 5 групп идёт схожими путями. Но они не здесь. Поэтому я действительно склонен считать, что в пределах RSDN мои задачи уникальны и специфика их — тоже. Возражать разрешаю только на собственных примерах;) G>Я понятия не имею чем ты занимаешь, но если завесу тайны уберешь, то найдется много людей кто занимается тем-же или похожим.
Никакой завесы. Текущая основная задача — система управления и мониторинга HPC кластеров. Основной код на Erlang, всякий клей на Python. Предыдущая — VoIP свич, в основном Python, местами C. Ещё на одну до того — вообще не программизм по сути, хотя много всякой ерунды на Perl и C для автоматизации работы и вокруг. Дальше в историю копать не буду, а то и до матфизики на Фортране докопаемся.
Ожидаю теперь такой же откровенности с твоей стороны:)
G>Посмотрев пару сотен сообщений в твоем профиле видно что большую часть ты тут пишешь в КСВ и о жизни, инода отвечая на темы о сетевых протоколах и С++.
C++? Вот именно в него я не лезу, по крайней мере вглубь.
G>Поэтому то чем ты занимаешься связано с сетями, точно не web, скорее всего linux\C++. Возможно что-то в высокой вычислительной нагрузкой типа аудио\видео конференц связи или обработкой видеопотоков от камер.
Linux — да. Ещё FreeBSD. C++ — нет. VoIP — было, но не сейчас. Вычислительная нагрузка — да. Конференц-связь — нет, бог миловал. Видеопотоки — тоже нет, не путай меня с Лапшиным;)
G>Учитывая что основной язык для тебя — C++,
Ну вот объясни мне — как можно было _так_ читать, чтобы прийти к такому выводу???
G> то там unit-тесты с трудом возможны, ибо компиляция долгая и нормальных mock-фреймворков нету.
А если не заниматься безумными гипотезами и учесть, что у меня Erlang и проблем с юнит-тестами в общем-то нет?
Mock'и подставить тривиально, это условия запуска в рантайме. Адекватная модульность обычно или заложена, или легко закладывается.
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, gandjustas, Вы писали:
N>>>>>Алгоритм — нет. А вот оптимальная реализация — да, может сильно измениться в зависимости от специфики запросов и обстановки. G>>>>Точно, поэтому и нужны unit-тесты чтобы проверить что новый и старый алгоритм эквивалентны N>>>Против этого я и не возражал. Вопрос в том, насколько они unit. G>>А что им мешает быть unit-тестами?
N>Зависит от глубины переделки. Может оказаться, что неизменна и покрыто неизменившимися тестами только самая внешняя функциональность, которая не может быть сведена к простому "выстрелил вызовом функции — получил ответ — проверил", а требует долгой подготовки среды. Для меня это всё-таки не unit тест. Unit — это когда максимум подготовки среды это расстановка заглушек вместо рабочего кода. Да, это деление не академично, но мне оно полезнее.
Вполне правильное деление. Вот только непонятно зачем переделка? Если не писались unit-тесты для кода, то скорее всего не будут написаны, а если будут то от этого будет много затрат без наблюдаемого эффекта.
N>>>>>Что ты называешь вариантами использования? G>>>>Это значит все возможные наборы параметров для которых имеются разные пути исполнения. N>>>Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно. G>>Вообще-то конечен. N>Ну числа типа 2**2**32 всё равно за пределами представления даже в BER, поэтому я позволил себе "округлить" их до бесконечности.
По факту их гораздо меньше.
G>>>>Не надо проверять все параметры. надо написать по одному тесту для каждого варианта использования. Их всегда конечное количество так как путей исполнения кода конечное количество. Как будешь вычислять эти самые варианты — твое дело, хоть автоматически с помощью pex, хоть анализируя код — без разницы. Вообще идеальный вариант — заранее продумывать как будет работать тот или иной метод класса и писать тесты. В любом случае вариантов использование будет очень ограниченное количество.
N>>>Под такое ещё надо адаптировать код. Далеко не всегда возможно, хотя, надо заметить, может быть полезно. Для того же примера с подсчётом длины строки, например, разделить код на функцию сдвига на один пункт и код подсчёта пунктов может быть полезно для ловли, например, ситуации, если промежуточная длина хранится в 8 битах (злобный пример того, как можно не заметить фигню на коротких тестах). Но это уже придётся mock'и делать.
G>>Если писать тесты до кода, то он как-то сам адаптируется
N>Писать тесты до кода означает или гарантированно окончательный дизайн, во что я не верю, или тупую наивность. По крайней мере я других вариантов пока не видел. Если есть где-то место, где это проходит, то там кому-то сильно повезло.
Ни разу не означает, потому что есть рефакторинг. Если не рефакторить код, то unit-тесты и не нужны, потому что любое изменение кода будет вызвано или исправлением бага, или изменением требований.
N>Я периодически использовал этот подход — тесты до кода — как стимулятор собственной работы против собственной же лени, то есть проблема была чисто организационная. Но я никогда не предполагал его как средство собственно дизайна. На это годится тестируемость в принципе, а не test-first.
Когда напишешь тест то на него уже не получается просто забить, особенно когда gated checkins включено. Если ты не пишешь заранее тесты, то рано или поздно у тебя получится забивание на тесты ради скорости написания, а потом тесты сделать, даже с мощными инструментами, очень сложно. Это ты кстати назвал "адаптацией".
G>>Пишешь простой тесты, пишешь для него простой код, простой код не удовлетворяет всем требованиям, пишешь еще один простой тест, правишь под два теста код, пишешь третий тест, начинаешь писать код, понимаешь что друге тесты надо править чтобы не падали, сразу возникает желание вынести код в другой класс\функцию\еще что-нить, а в данном классе оставить mock. G>>Для TDD (test-first) это естественный процесс, для test-after — нет. Поэтому unit-тесты и рекомендуется для test-first.
N>Не так. Оцениваешь функциональность. Пишешь прототип, или даже просто схему на бумаге, что должно делаться и как. Смотришь на него, проводишь фактически design review (хоть в одиночку, но можно и с коллегами), оцениваешь, что и как реализовывать. На этом этапе могут выделяться чёткие подблоки, которые пригодны к тестированию отдельно от остального. Но ещё не под тест, потому что могут интерфейсы и контракты поменяться уже в процессе реализации, когда будет осознан ещё один пласт специфики. Потом всё это приблизительно написано, разделение на составные части, интерфейсы, контракты стабилизировались, теперь нетривиальные внутренние части покрываются базовыми тестами, подтверждающими их работоспособность. В основном это юнит-тесты, с минимальной настройкой среды проверки или вообще без неё, на уровне отдельных функций. Далее рассчитываешь и делаешь функциональные тесты, под базовые юзкейсы компонент, вычищаешь их от багов. В моём случае обычно минимальная единица, обладающая собственным поведением, называется приложением, и делать функциональные тесты для более мелких частей обычно нереально (хотя бывают и такие сущности). На следующем уровне тестируются связки из таких приложений, образующие функциональность части полной системы, например, тракт вокруг конкретной шины от первичных генераторов событий до финальных накопителей; назовём это интеграционными тестами. Только после этого можно говорить о завершении разработки конкретной подсистемы или даже приложения — когда оно показывает корректное прохождение интеграционных тестов. Далее идут общесистемные тесты, это уже делается людьми в QA отделе. Процесс такого развития никак не соответствует продвижению сверху вниз с твёрдой уверенностью в функционале конкретного уровня, а вместо этого можно говорить только о приблизительной чёткости понимания, которая будет доведена до полной уже после пробы на прототипе. Вот-с, где-то так.
Многабукафниасилил. Но чтение по диагонали говорит что правильно пишешь, только гораздо шире чем я. Выделенное примерно соответствует тому что я написал, остальное out-of-scope. На этапе прототипирования тесты не нужны. Вообще методика test-first заставляет создавать приложение по слоям сверху вниз. Если заранее не знаешь как оно должно быть, то пишешь прототип без тестов, а потом уже переписываешь (именно переписываешь, нет смысла рефакторить прототип). Это в терминологии называются spike.
N>>>А без адаптации, по крайней мере для моей обстановки, никак не получается "очень ограниченное количество" ситуаций. Поэтому для меня whitebox testing имеет совсем другой смысл (см. предыдущие письма). G>>То есть изначально написан плохо тестируемый код. Но эту ситуацию ты экстраполируешь на другие случаи, что далеко неверно.
N>Я продолжу эксплуатировать тот же пример — подсчёт codepoints. Ты действительно считаешь, что если функцию такого рода нельзя разделить на части, проверяемые по методу такого whitebox и подменяемые mock'ами, то это "изначально плохо тестируемый код"? Ответь, пожалуйста, конкретно, а не в сторону. Мне таки очень интересно. Пример простой, но полезный.
Нет, я считаю только одно: если код для тестирования надо адаптировать, то он "изначально плохо тестируемый".
N>>>Ты путаешь совершенно разные вещи. Ошибки часто одинаковые, это да. Специфика не уникальна, но она колоссально разная. Я не могу найти сейчас человека здесь на форуме, который заметен и который занимается хотя бы примерно теми же задачами, что я. Если есть, то они не светятся. В то же время я знаю, что в мире ещё около 5 групп идёт схожими путями. Но они не здесь. Поэтому я действительно склонен считать, что в пределах RSDN мои задачи уникальны и специфика их — тоже. Возражать разрешаю только на собственных примерах G>>Я понятия не имею чем ты занимаешь, но если завесу тайны уберешь, то найдется много людей кто занимается тем-же или похожим.
N>Никакой завесы. Текущая основная задача — система управления и мониторинга HPC кластеров. Основной код на Erlang, всякий клей на Python. Предыдущая — VoIP свич, в основном Python, местами C. Ещё на одну до того — вообще не программизм по сути, хотя много всякой ерунды на Perl и C для автоматизации работы и вокруг. Дальше в историю копать не буду, а то и до матфизики на Фортране докопаемся.
N>Ожидаю теперь такой же откровенности с твоей стороны
А я вообще руководитель небольшой фирмы, немного тренер, за последний месяц не более сотни строк написал production кода, в основном ТЗ всякие, да коммерческие приложения.
G>>Учитывая что основной язык для тебя — C++, N>Ну вот объясни мне — как можно было _так_ читать, чтобы прийти к такому выводу???
Сорри, сейчас редко встретишь тех кто пишет на С, а не С++
G>> то там unit-тесты с трудом возможны, ибо компиляция долгая и нормальных mock-фреймворков нету. N>А если не заниматься безумными гипотезами и учесть, что у меня Erlang и проблем с юнит-тестами в общем-то нет? N>Mock'и подставить тривиально, это условия запуска в рантайме. Адекватная модульность обычно или заложена, или легко закладывается.
Так эрланг же динамически типизированный, там вообще необходимо тесты писать, п-другому не проверишь.
Но к сожалению мои познания в erlang низкие и ниче сказать не могу.
Здравствуйте, gandjustas, Вы писали:
N>>>>Против этого я и не возражал.:) Вопрос в том, насколько они unit. G>>>А что им мешает быть unit-тестами? N>>Зависит от глубины переделки. Может оказаться, что неизменна и покрыто неизменившимися тестами только самая внешняя функциональность, которая не может быть сведена к простому "выстрелил вызовом функции — получил ответ — проверил", а требует долгой подготовки среды. Для меня это всё-таки не unit тест. Unit — это когда максимум подготовки среды это расстановка заглушек вместо рабочего кода. Да, это деление не академично, но мне оно полезнее.
G>Вполне правильное деление. Вот только непонятно зачем переделка? Если не писались unit-тесты для кода, то скорее всего не будут написаны, а если будут то от этого будет много затрат без наблюдаемого эффекта.
Зачем переделка — надо спросить авторов подветки. Собственно она началась с того, что решался вопрос, нужны юнит-тесты или нет, если будет большой рефакторинг, который поменяет всё внутри. Ты был один из авторов, так что отвечай сам на этот вопрос:)
N>>>>>>Что ты называешь вариантами использования? G>>>>>Это значит все возможные наборы параметров для которых имеются разные пути исполнения. N>>>>Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно. G>>>Вообще-то конечен. N>>Ну числа типа 2**2**32 всё равно за пределами представления даже в BER:), поэтому я позволил себе "округлить" их до бесконечности. G>По факту их гораздо меньше.
Уверен? Только на своих примерах?
G>>>Если писать тесты до кода, то он как-то сам адаптируется ;) N>>Писать тесты до кода означает или гарантированно окончательный дизайн, во что я не верю, или тупую наивность. По крайней мере я других вариантов пока не видел. Если есть где-то место, где это проходит, то там кому-то сильно повезло. G>Ни разу не означает, потому что есть рефакторинг. Если не рефакторить код, то unit-тесты и не нужны, потому что любое изменение кода будет вызвано или исправлением бага, или изменением требований.
Не вижу никакой логической связи между наличием или отсутствием рефакторинга и необходимости в юнит-тестах. Юнит-тест это функциональный тест уровня модуля, не имеющего самостоятельного поведения в системе. Тест нужен для того, чтобы быть уверенным в соответствии кода поставленным требованиям (как бы они ни назывались: ТЗ, контракты или шкурой чёрта лысого). Понятие, наличие и влияние рефакторинга полностью ортогонально этому.
N>>Я периодически использовал этот подход — тесты до кода — как стимулятор собственной работы против собственной же лени, то есть проблема была чисто организационная. Но я никогда не предполагал его как средство собственно дизайна. На это годится тестируемость в принципе, а не test-first. G>Когда напишешь тест то на него уже не получается просто забить, особенно когда gated checkins включено.
У нас нет никаких gated checkins. Но даже если бы были, это означало бы опять же, что введено административное требование соблюдать какие-то оформительские требования, не более того, и цена ему, как любому подобному административному требованию — высокая, если оно разумно обусловлено ситуацией, и ломаный грош в противном случае.
G> Если ты не пишешь заранее тесты, то рано или поздно у тебя получится забивание на тесты ради скорости написания, а потом тесты сделать, даже с мощными инструментами, очень сложно. Это ты кстати назвал "адаптацией".
Вопрос не в мощных инструментах, вопрос в понимании кода. Нужно вспомнить и войти в контекст. И это не сложно, это просто вопрос времени, которого может на это не быть.
N>>Не так. Оцениваешь функциональность. Пишешь прототип, или даже просто схему на бумаге, что должно делаться и как. Смотришь на него, проводишь фактически design review (хоть в одиночку, но можно и с коллегами), оцениваешь, что и как реализовывать. На этом этапе могут выделяться чёткие подблоки, которые пригодны к тестированию отдельно от остального. Но ещё не под тест, потому что могут интерфейсы и контракты поменяться уже в процессе реализации, когда будет осознан ещё один пласт специфики. Потом всё это приблизительно написано, разделение на составные части, интерфейсы, контракты стабилизировались, теперь нетривиальные внутренние части покрываются базовыми тестами, подтверждающими их работоспособность. В основном это юнит-тесты, с минимальной настройкой среды проверки или вообще без неё, на уровне отдельных функций. Далее рассчитываешь и делаешь функциональные тесты, под базовые юзкейсы компонент, вычищаешь их от багов. В моём случае обычно минимальная единица, обладающая собственным поведением, называется приложением, и делать функциональные тесты для более мелких частей обычно нереально (хотя бывают и такие сущности). На следующем уровне тестируются связки из таких приложений, образующие функциональность части полной системы, например, тракт вокруг конкретной шины от первичных генераторов событий до финальных накопителей; назовём это интеграционными тестами. Только после этого можно говорить о завершении разработки конкретной подсистемы или даже приложения — когда оно показывает корректное прохождение интеграционных тестов. Далее идут общесистемные тесты, это уже делается людьми в QA отделе. Процесс такого развития никак не соответствует продвижению сверху вниз с твёрдой уверенностью в функционале конкретного уровня, а вместо этого можно говорить только о приблизительной чёткости понимания, которая будет доведена до полной уже после пробы на прототипе. Вот-с, где-то так.
G>Многабукафниасилил. Но чтение по диагонали говорит что правильно пишешь, только гораздо шире чем я. Выделенное примерно соответствует тому что я написал, остальное out-of-scope. На этапе прототипирования тесты не нужны. Вообще методика test-first заставляет создавать приложение по слоям сверху вниз. Если заранее не знаешь как оно должно быть, то пишешь прототип без тестов, а потом уже переписываешь (именно переписываешь, нет смысла рефакторить прототип). Это в терминологии называются spike.
Всё это замечательно, но ещё проще и удобнее делать это же без жёсткого test-first :)
N>>Я продолжу эксплуатировать тот же пример — подсчёт codepoints. Ты действительно считаешь, что если функцию такого рода нельзя разделить на части, проверяемые по методу такого whitebox и подменяемые mock'ами, то это "изначально плохо тестируемый код"? Ответь, пожалуйста, конкретно, а не в сторону. Мне таки очень интересно. Пример простой, но полезный. G>Нет, я считаю только одно: если код для тестирования надо адаптировать, то он "изначально плохо тестируемый".
"Жаль, что мы так и не заслушали начальника транспортного цеха."
G>>>Учитывая что основной язык для тебя — C++, N>>Ну вот объясни мне — как можно было _так_ читать, чтобы прийти к такому выводу??? G>Сорри, сейчас редко встретишь тех кто пишет на С, а не С++
Так у меня на >99% и не C. :)
G>>> то там unit-тесты с трудом возможны, ибо компиляция долгая и нормальных mock-фреймворков нету. N>>А если не заниматься безумными гипотезами и учесть, что у меня Erlang и проблем с юнит-тестами в общем-то нет? N>>Mock'и подставить тривиально, это условия запуска в рантайме. Адекватная модульность обычно или заложена, или легко закладывается. G>Так эрланг же динамически типизированный, там вообще необходимо тесты писать, п-другому не проверишь.
Местных немерлистов наслушался?;) Проблемы непроверяемости при динамической типизации сильно преувеличены или относятся к отдельным областям, которые для меня диковинка. В случае Erlang даже dialyzer, производящий статический контроль типов, помогает выловить некоторое количество плюх, но основные проблемы не от них.
Здравствуйте, netch80, Вы писали:
N>Здравствуйте, gandjustas, Вы писали:
N>>>>>Против этого я и не возражал. Вопрос в том, насколько они unit. G>>>>А что им мешает быть unit-тестами? N>>>Зависит от глубины переделки. Может оказаться, что неизменна и покрыто неизменившимися тестами только самая внешняя функциональность, которая не может быть сведена к простому "выстрелил вызовом функции — получил ответ — проверил", а требует долгой подготовки среды. Для меня это всё-таки не unit тест. Unit — это когда максимум подготовки среды это расстановка заглушек вместо рабочего кода. Да, это деление не академично, но мне оно полезнее.
G>>Вполне правильное деление. Вот только непонятно зачем переделка? Если не писались unit-тесты для кода, то скорее всего не будут написаны, а если будут то от этого будет много затрат без наблюдаемого эффекта.
N>Зачем переделка — надо спросить авторов подветки. Собственно она началась с того, что решался вопрос, нужны юнит-тесты или нет, если будет большой рефакторинг, который поменяет всё внутри. Ты был один из авторов, так что отвечай сам на этот вопрос
вообще не понимаю как рефакторинг без тестов делать, нужно знать что поведение не поменяется, кроме тестов тут мало что поможет.
Но если их заранее не было, то для их написания надо будет еще переделать код, а это двойные затраты.
Проще переписать кусок, предварительно написав для него тесты.
N>>>>>>>Что ты называешь вариантами использования? G>>>>>>Это значит все возможные наборы параметров для которых имеются разные пути исполнения. N>>>>>Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно. G>>>>Вообще-то конечен. N>>>Ну числа типа 2**2**32 всё равно за пределами представления даже в BER, поэтому я позволил себе "округлить" их до бесконечности. G>>По факту их гораздо меньше.
N>Уверен? Только на своих примерах?
На любых. Есть хорошая утилита pex, которая как раз показывает такие пути. Запускал её давно на коде, который писался без тестов. Метод на 200 строк (метрика, исходников почти 1000 строк) — около 15 путей. То есть для полного покрытия надо не более 15 тестов.
Даже если взять 30 тестов на 1000 строк исходников, то это не дофига. А если применять TDD, то будет еще меньше.
G>>>>Если писать тесты до кода, то он как-то сам адаптируется N>>>Писать тесты до кода означает или гарантированно окончательный дизайн, во что я не верю, или тупую наивность. По крайней мере я других вариантов пока не видел. Если есть где-то место, где это проходит, то там кому-то сильно повезло. G>>Ни разу не означает, потому что есть рефакторинг. Если не рефакторить код, то unit-тесты и не нужны, потому что любое изменение кода будет вызвано или исправлением бага, или изменением требований.
N>Не вижу никакой логической связи между наличием или отсутствием рефакторинга и необходимости в юнит-тестах. Юнит-тест это функциональный тест уровня модуля, не имеющего самостоятельного поведения в системе. Тест нужен для того, чтобы быть уверенным в соответствии кода поставленным требованиям (как бы они ни назывались: ТЗ, контракты или шкурой чёрта лысого). Понятие, наличие и влияние рефакторинга полностью ортогонально этому.
Связь сама прямая. Рефакторинг — изменение (улучшение) кода, без изменения наблюдаемого поведения. Как гарантировать неизменность наблюдаемого поведения?
N>>>Я периодически использовал этот подход — тесты до кода — как стимулятор собственной работы против собственной же лени, то есть проблема была чисто организационная. Но я никогда не предполагал его как средство собственно дизайна. На это годится тестируемость в принципе, а не test-first. G>>Когда напишешь тест то на него уже не получается просто забить, особенно когда gated checkins включено.
N>У нас нет никаких gated checkins. Но даже если бы были, это означало бы опять же, что введено административное требование соблюдать какие-то оформительские требования, не более того, и цена ему, как любому подобному административному требованию — высокая, если оно разумно обусловлено ситуацией, и ломаный грош в противном случае.
На gated checkins тесты запускается и код не коммитится если они надают.
G>> Если ты не пишешь заранее тесты, то рано или поздно у тебя получится забивание на тесты ради скорости написания, а потом тесты сделать, даже с мощными инструментами, очень сложно. Это ты кстати назвал "адаптацией".
N>Всё это замечательно, но ещё проще и удобнее делать это же без жёсткого test-first
TDD не предполагает test-first всегда.
Здравствуйте, gandjustas, Вы писали:
N>>Зачем переделка — надо спросить авторов подветки. Собственно она началась с того, что решался вопрос, нужны юнит-тесты или нет, если будет большой рефакторинг, который поменяет всё внутри. Ты был один из авторов, так что отвечай сам на этот вопрос:) G>вообще не понимаю как рефакторинг без тестов делать, нужно знать что поведение не поменяется, кроме тестов тут мало что поможет.
Гм. Кто-то тут преимущества статических языков вспоминал:) почему-то:)
N>>>>>>>>Что ты называешь вариантами использования? G>>>>>>>Это значит все возможные наборы параметров для которых имеются разные пути исполнения. N>>>>>>Для меня такое тестирование имеет очень мало смысла, только для отдельных специфических вариантов функций. Полный набор путей исполнения, мягко говоря, бесконечен. Можно было бы только в целях тестирования выделить отдельные логические подблоки типа "продвинуться на шаг алгоритма", выделяя их в функции, но часто это слишком неудобно. G>>>>>Вообще-то конечен. N>>>>Ну числа типа 2**2**32 всё равно за пределами представления даже в BER:), поэтому я позволил себе "округлить" их до бесконечности. G>>>По факту их гораздо меньше. N>>Уверен? Только на своих примерах? G>На любых. Есть хорошая утилита pex, которая как раз показывает такие пути. Запускал её давно на коде, который писался без тестов. Метод на 200 строк (метрика, исходников почти 1000 строк) — около 15 путей. То есть для полного покрытия надо не более 15 тестов. G>Даже если взять 30 тестов на 1000 строк исходников, то это не дофига. А если применять TDD, то будет еще меньше.
У тебя есть PEX? Мне его поставить некуда, Win7 даже в кошмарах не снится. Напусти его, например, на библиотечный qsort(). mbslen(), если он сам реализован, а не через виндовый рантайм. Больше вариантов пока не вижу, потому что юниксовых функций у вас нет;) И тогда посмотрим, насколько он ценен:) Мне искренне интересно — если там что-то ценное, можно будет применить или хотя бы найти аналог.
G>>>>>Если писать тесты до кода, то он как-то сам адаптируется ;) N>>>>Писать тесты до кода означает или гарантированно окончательный дизайн, во что я не верю, или тупую наивность. По крайней мере я других вариантов пока не видел. Если есть где-то место, где это проходит, то там кому-то сильно повезло. G>>>Ни разу не означает, потому что есть рефакторинг. Если не рефакторить код, то unit-тесты и не нужны, потому что любое изменение кода будет вызвано или исправлением бага, или изменением требований. N>>Не вижу никакой логической связи между наличием или отсутствием рефакторинга и необходимости в юнит-тестах. Юнит-тест это функциональный тест уровня модуля, не имеющего самостоятельного поведения в системе. Тест нужен для того, чтобы быть уверенным в соответствии кода поставленным требованиям (как бы они ни назывались: ТЗ, контракты или шкурой чёрта лысого). Понятие, наличие и влияние рефакторинга полностью ортогонально этому. G>Связь сама прямая. Рефакторинг — изменение (улучшение) кода, без изменения наблюдаемого поведения. Как гарантировать неизменность наблюдаемого поведения?
Мой вопрос был в слове _юнит_. Тесты бывают разные. Программа состоит из множества уровней реализации. Могут быть отдельные функции, модули, группы модулей, приложения (в нашем случае — именно на этом уровне), подсистемы, системы. Для каждого из них может быть свой уровень теста. Всё, что выше модуля — это уже не юнит-тесты. Но рефакторинг может, например, слить две подсистемы в одну, или разделить их. Чем тебе тут помогут именно юнит-тесты?
N>>>>Я периодически использовал этот подход — тесты до кода — как стимулятор собственной работы против собственной же лени, то есть проблема была чисто организационная. Но я никогда не предполагал его как средство собственно дизайна. На это годится тестируемость в принципе, а не test-first. G>>>Когда напишешь тест то на него уже не получается просто забить, особенно когда gated checkins включено. N>>У нас нет никаких gated checkins. Но даже если бы были, это означало бы опять же, что введено административное требование соблюдать какие-то оформительские требования, не более того, и цена ему, как любому подобному административному требованию — высокая, если оно разумно обусловлено ситуацией, и ломаный грош в противном случае. G>На gated checkins тесты запускается и код не коммитится если они надают.
"О наблюдении за наблюдающими за наблюдателями" ((c)): кто гарантирует, что набор тестов адекватен?
G>>> Если ты не пишешь заранее тесты, то рано или поздно у тебя получится забивание на тесты ради скорости написания, а потом тесты сделать, даже с мощными инструментами, очень сложно. Это ты кстати назвал "адаптацией". N>>Всё это замечательно, но ещё проще и удобнее делать это же без жёсткого test-first :) G>TDD не предполагает test-first всегда.
Test-driven development requires developers to create automated unit tests that define code requirements (immediately) before writing the code itself.
Это из википедии. Если ты пользуешься другим источником определений — пожалуйста, но определи его заранее, чтобы не было разногласий только из-за этого.
Здравствуйте, netch80, Вы писали:
G>>Даже если взять 30 тестов на 1000 строк исходников, то это не дофига. А если применять TDD, то будет еще меньше. N>У тебя есть PEX? Мне его поставить некуда, Win7 даже в кошмарах не снится. Напусти его, например, на библиотечный qsort(). mbslen(), если он сам реализован, а не через виндовый рантайм. Больше вариантов пока не вижу, потому что юниксовых функций у вас нет И тогда посмотрим, насколько он ценен Мне искренне интересно — если там что-то ценное, можно будет применить или хотя бы найти аналог.
Конечно есть, то что ты его поставить не можешь — твои проблемы. pex работает только для .NET, и разработчики BCL при написании четвертной версии натравливали pex на библиотеки. Говорили что находили баги о которых ранее не подозревали. Насчет аналогичных инструментов для C не знаю, вряд ли будет. Он слишком близок к ассемблеру, вряд ли там что-то можно проверить. Хотя есть верифицирующий компилятор.
G>>Связь сама прямая. Рефакторинг — изменение (улучшение) кода, без изменения наблюдаемого поведения. Как гарантировать неизменность наблюдаемого поведения? N>Мой вопрос был в слове _юнит_. Тесты бывают разные. Программа состоит из множества уровней реализации. Могут быть отдельные функции, модули, группы модулей, приложения (в нашем случае — именно на этом уровне), подсистемы, системы. Для каждого из них может быть свой уровень теста. Всё, что выше модуля — это уже не юнит-тесты. Но рефакторинг может, например, слить две подсистемы в одну, или разделить их. Чем тебе тут помогут именно юнит-тесты?
А может и не слить. Гораздо больше маленьких рефакторингов выполняется, чем больших. Причем далеко не все из них занимаются механическим изменением код, которое можно автоматизировать.
G>>На gated checkins тесты запускается и код не коммитится если они надают. N>"О наблюдении за наблюдающими за наблюдателями" ((c)): кто гарантирует, что набор тестов адекватен?
Ты гарантируешь, когда пишешь тест. Иначе никак. Юнит-тесты кстати очень хороши, так как будут простыми и линейными.
G>>>> Если ты не пишешь заранее тесты, то рано или поздно у тебя получится забивание на тесты ради скорости написания, а потом тесты сделать, даже с мощными инструментами, очень сложно. Это ты кстати назвал "адаптацией". N>>>Всё это замечательно, но ещё проще и удобнее делать это же без жёсткого test-first G>>TDD не предполагает test-first всегда.
N>
Test-driven development requires developers to create automated unit tests that define code requirements (immediately) before writing the code itself.
Это из википедии. Если ты пользуешься другим источником определений — пожалуйста, но определи его заранее, чтобы не было разногласий только из-за этого.
Это черезмерное упрощение, примерно как ООП — данные+методы (см соседнюю тему).
Здравствуйте, gandjustas, Вы писали:
G>>>Даже если взять 30 тестов на 1000 строк исходников, то это не дофига. А если применять TDD, то будет еще меньше. N>>У тебя есть PEX? Мне его поставить некуда, Win7 даже в кошмарах не снится. Напусти его, например, на библиотечный qsort(). mbslen(), если он сам реализован, а не через виндовый рантайм. Больше вариантов пока не вижу, потому что юниксовых функций у вас нет;) И тогда посмотрим, насколько он ценен:) Мне искренне интересно — если там что-то ценное, можно будет применить или хотя бы найти аналог. G>Конечно есть, то что ты его поставить не можешь — твои проблемы.
Ты всерьёз думаешь, что это мои _проблемы_? Я так не думаю, он мне нафиг не нужен кроме как для этой дискуссии:)
G> pex работает только для .NET,
Значит, в морг.
G>>>Связь сама прямая. Рефакторинг — изменение (улучшение) кода, без изменения наблюдаемого поведения. Как гарантировать неизменность наблюдаемого поведения? N>>Мой вопрос был в слове _юнит_. Тесты бывают разные. Программа состоит из множества уровней реализации. Могут быть отдельные функции, модули, группы модулей, приложения (в нашем случае — именно на этом уровне), подсистемы, системы. Для каждого из них может быть свой уровень теста. Всё, что выше модуля — это уже не юнит-тесты. Но рефакторинг может, например, слить две подсистемы в одну, или разделить их. Чем тебе тут помогут именно юнит-тесты? G>А может и не слить. Гораздо больше маленьких рефакторингов выполняется, чем больших. Причем далеко не все из них занимаются механическим изменением код, которое можно автоматизировать.
Значит, ответа не будет. Ясно.
G>>>На gated checkins тесты запускается и код не коммитится если они надают. N>>"О наблюдении за наблюдающими за наблюдателями" ((c)): кто гарантирует, что набор тестов адекватен? G>Ты гарантируешь, когда пишешь тест. Иначе никак.
Quod erat demonstrandum.
G>>>>> Если ты не пишешь заранее тесты, то рано или поздно у тебя получится забивание на тесты ради скорости написания, а потом тесты сделать, даже с мощными инструментами, очень сложно. Это ты кстати назвал "адаптацией". N>>>>Всё это замечательно, но ещё проще и удобнее делать это же без жёсткого test-first :) G>>>TDD не предполагает test-first всегда. N>>
Test-driven development requires developers to create automated unit tests that define code requirements (immediately) before writing the code itself.
Это из википедии. Если ты пользуешься другим источником определений — пожалуйста, но определи его заранее, чтобы не было разногласий только из-за этого. G>Это черезмерное упрощение, примерно как ООП — данные+методы (см соседнюю тему).
Видимо, есть какое-то тайное знание, которое мне знать не положено — рылом не вышел. OK.
G>>>>Связь сама прямая. Рефакторинг — изменение (улучшение) кода, без изменения наблюдаемого поведения. Как гарантировать неизменность наблюдаемого поведения? N>>>Мой вопрос был в слове _юнит_. Тесты бывают разные. Программа состоит из множества уровней реализации. Могут быть отдельные функции, модули, группы модулей, приложения (в нашем случае — именно на этом уровне), подсистемы, системы. Для каждого из них может быть свой уровень теста. Всё, что выше модуля — это уже не юнит-тесты. Но рефакторинг может, например, слить две подсистемы в одну, или разделить их. Чем тебе тут помогут именно юнит-тесты? G>>А может и не слить. Гораздо больше маленьких рефакторингов выполняется, чем больших. Причем далеко не все из них занимаются механическим изменением код, которое можно автоматизировать. N>Значит, ответа не будет. Ясно.
Ответа на что? Ты выдвинул предположение что при масштабных рефакторингах юнит-тесты не помогают. Я в принципе согласен с тобой, но все равно это не повод не писать их.
Опять приходим к простому выводу: если ты не видишь пользы в юнит-тестах — не пиши их. Возможно у тебя приложение уже написано так что юнит-тестами не покрывается и\или язык неподходящий. Но это все не проблема юнит-тестов.
G>>>>>> Если ты не пишешь заранее тесты, то рано или поздно у тебя получится забивание на тесты ради скорости написания, а потом тесты сделать, даже с мощными инструментами, очень сложно. Это ты кстати назвал "адаптацией". N>>>>>Всё это замечательно, но ещё проще и удобнее делать это же без жёсткого test-first G>>>>TDD не предполагает test-first всегда. N>>>
Test-driven development requires developers to create automated unit tests that define code requirements (immediately) before writing the code itself.
Это из википедии. Если ты пользуешься другим источником определений — пожалуйста, но определи его заранее, чтобы не было разногласий только из-за этого. G>>Это черезмерное упрощение, примерно как ООП — данные+методы (см соседнюю тему).
N>Видимо, есть какое-то тайное знание, которое мне знать не положено — рылом не вышел. OK.
Читать надо, я ссылки не записываю, иначе тебе подкинул бы. Если твое познание в TDD (да или в любом другом предмете) ограничивается википедией, то неудивительно что рано или поздно тебе сообщают вещи, о которых ты не знал.
Здравствуйте, gandjustas, Вы писали:
G>>>>>TDD не предполагает test-first всегда. N>>>>
Test-driven development requires developers to create automated unit tests that define code requirements (immediately) before writing the code itself.
Это из википедии. Если ты пользуешься другим источником определений — пожалуйста, но определи его заранее, чтобы не было разногласий только из-за этого. G>>>Это черезмерное упрощение, примерно как ООП — данные+методы (см соседнюю тему). N>>Видимо, есть какое-то тайное знание, которое мне знать не положено — рылом не вышел. OK. G>Читать надо, я ссылки не записываю, иначе тебе подкинул бы. Если твое познание в TDD (да или в любом другом предмете) ограничивается википедией, то неудивительно что рано или поздно тебе сообщают вещи, о которых ты не знал.
Ну продолжай сражаться с ветряными мельницами ("если" какое-то совершенно отфонарное предположение, "то" ты успешно победил врага в моём лице, я якобы в глубокой заднице, сижу и хнычу;)) Только без меня, OK? Ты продолжаешь косвенно ссылаться на тайное знание, источников не приводишь (видите ли, "я ссылки не записываю"), и ладно бы речь ещё шла о каком-то хитром аспекте — тогда ещё имеет смысл говорить об уровне опыта и о знании неожиданностей. Но ты пытаешься навязать мне свою нормировку базовых принципов (а обязательность test-first это таки базовый принцип, самый базовый из) и при этом ссылаешься на тайное знание. Извини, на такое ответ только один — или приводи то, что может быть названо с какой-то точки зрения стандартом (если нет нормативного документа от какого-нибудь IEEE, ISO, IEC или просто ГОСТ, то и википедия сойдёт), или отправляйся в пешее эротическое путешествие, ибо мне твои иллюзии о нормативах нафиг не сдались.
Собственно, это касается и остальной части сообщения: мне надоело общаться с твоими иллюзиями, баста.
Здравствуйте, ylem, Вы писали:
Y>Может я неправильно все делаю, но именно юнит-тестами удобно отлаживать сколь-нибудь сложную логику. Y>Ну а потом они просто остаются. Есть пить почти не просят.
В разных случаях, рулят разные вещи. Например, при написании компиляторов юнит-тесты имеют смысл только на самых ранних стадиях. Далее в них смысла нет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, ylem, Вы писали:
Y>>Может я неправильно все делаю, но именно юнит-тестами удобно отлаживать сколь-нибудь сложную логику. Y>>Ну а потом они просто остаются. Есть пить почти не просят.
VD>В разных случаях, рулят разные вещи. Например, при написании компиляторов юнит-тесты имеют смысл только на самых ранних стадиях. Далее в них смысла нет.
Здравствуйте, Abyx, Вы писали:
VD>>В разных случаях, рулят разные вещи. Например, при написании компиляторов юнит-тесты имеют смысл только на самых ранних стадиях. Далее в них смысла нет.
A>почему нет?
Потому что нет смысла.
Зачем писать синтетический тест тестирующий отдельную функцию когда можно написать программу компиляция и запуск которой проверит эту функцию в сто раз лучше?
Язык программирования — это такая штука которая при небольшом количестве базовых блоков дает невероятный комбинаторный взрыв. Ведь почти на любом языке можно написать почти любую программу.
На мой взгляд основная задача тестов (в независимости от их типа) — фиксация и описание поведения. Мы не можем описать поведение через систему типов. И тесты становятся эдаким живым описанием поведения. Ну, а ловля ошибок при рефакторинге — это уже следствие данной фиксации. Программные продукты вещь сложная. Всего не запомнишь. Если зафиксировать некоторую фичу в виде теста, то потом можно полагаться на данную фиксацию.
И совершенно по фигу каким видом тестов мы зафиксировали это самое поведение.
Так же совершенно все равно пишется ли тест до или после написания функциональности. Это предпочтения программиста. Бывают случае когда писать тест до удобнее. Например, при исправлении ошибки. Но бывает, случае когда в процессе написания кода производится исследовательская деятельность. При этом код может меняться по сто раз на дню. И пытаться написать тест для еще не сформировавшегося решения — это самое глупое что только можно придумать.
В общем, фанатизм никогда не дает хороших результатов. В каждом отдельном случае нужно выбирать свои подходы. От того для меня фанатики TDD и те кто орет что тесты не нужны мало чем отличаются. Это люди из одного лагеря — лагеря людей не способных предатель своих предрассудков.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Abyx, Вы писали:
VD>>>В разных случаях, рулят разные вещи. Например, при написании компиляторов юнит-тесты имеют смысл только на самых ранних стадиях. Далее в них смысла нет.
A>>почему нет?
VD>Потому что нет смысла.
VD>Зачем писать синтетический тест тестирующий отдельную функцию когда можно написать программу компиляция и запуск которой проверит эту функцию в сто раз лучше?
VD>Язык программирования — это такая штука которая при небольшом количестве базовых блоков дает невероятный комбинаторный взрыв. Ведь почти на любом языке можно написать почти любую программу.
Я не верю, что можно написать тестовые программы которые дадут 100% покрытия.
Даже если это возможно, они покроют только удачные сценарии.
Если код компилятора состоит из потенциально реюзабельных частей, они будут делать чуть больше чем требуется в текущей версии программы:
void foo(X* p)
{
if (p == NULL)
bar();
...
}
// единственное использование foo во всей программе (на данный момент)
X x;
foo(&x);
— тут вызов bar() это вообще мертвый код, его может даже не быть в бинарнике программы.
VD>Так же совершенно все равно пишется ли тест до или после написания функциональности. Это предпочтения программиста. Бывают случае когда писать тест до удобнее. Например, при исправлении ошибки. Но бывает, случае когда в процессе написания кода производится исследовательская деятельность. При этом код может меняться по сто раз на дню. И пытаться написать тест для еще не сформировавшегося решения — это самое глупое что только можно придумать.
прототипирование — это отдельный случай.
когда решение сформировалось, тогда уже можно написать тест и затем написать код начисто
Здравствуйте, Abyx, Вы писали:
VD>>Язык программирования — это такая штука которая при небольшом количестве базовых блоков дает невероятный комбинаторный взрыв. Ведь почти на любом языке можно написать почти любую программу.
A>Я не верю, что можно написать тестовые программы которые дадут 100% покрытия.
И правильно делаешь. Потому в любом компиляторе есть ошибки.
A>Даже если это возможно, они покроют только удачные сценарии.
Тут вопрос в другом. То что покрыто является спецификацией поддерживаемой функциональности. По уму дюбсая описанная фича должна иметь один или более тестов.
Я сам не раз нарывался на то, что пытаясь решить некоторую проблему менял (на первый взгляд, незначительно) поведение компилятора и это приводило к падению тестов. Когда я начианал разбираться, то чаще всего оказывалось, что есть поведение о котором я не знал или знал, но не предполагал, что оно может повлиять на данный случай. Если бы таких тестов не было, то я бы испортил компилятор.
Кроме того на тесты можно смотреть как на "спецификацию на примерах". Она значительно надежнее и точнее чем описание того же самого на естественном языке.
A>Если код компилятора состоит из потенциально реюзабельных частей, они будут делать чуть больше чем требуется в текущей версии программы:... A>- тут вызов bar() это вообще мертвый код, его может даже не быть в бинарнике программы.
Зачем обсуждать какие-то гипотетические вещи когда есть реальные тесты? Вот позитивные тесты. Позитивные означает, что они должны пройти успешно не выдав сообщений об ошибках, не свалив компилятор и их выхлоп должен совпасть с описанным в комментариях. Принцип очень простой. Мы компилируем исходник. Если все ОК, запускаем его и сравниваем его консольный выхлоп с тем что описан в комментарии (между BEGIN-OUTPUT и END-OUTPUT). Если блока BEGIN-OUTPUT/END-OUTPUT нет, то проверяется только сам факт компилируемости программы.
Так же есть негативные тесты. Их задача проверить реакцию на наличие ошибок. Ошибки описываются в виде регексов в комментариях в тех строках где они должны появиться.
VD>>Так же совершенно все равно пишется ли тест до или после написания функциональности. Это предпочтения программиста. Бывают случае когда писать тест до удобнее. Например, при исправлении ошибки. Но бывает, случае когда в процессе написания кода производится исследовательская деятельность. При этом код может меняться по сто раз на дню. И пытаться написать тест для еще не сформировавшегося решения — это самое глупое что только можно придумать.
A>прототипирование — это отдельный случай. A>когда решение сформировалось, тогда уже можно написать тест и затем написать код начисто
Когда решение сформулировано, то у нас уже есть решение и ты уже не можешь сначала написать тест, а потом реализацию. Об этом я и говорил.
Могу описать то как я пишу тесты для того же компилятора.
Если я ловлю ошибку, то первым делом я пытаюсь создать минимальный тест воспроизводящий ошибку. Если таковой нельзя создать, то возможно, что и отлов ошибки будет отложен в долгий ящик. И тест я пишу не из религиозных соображений, а просто потому, что без него мне нечего будет отлаживать. На этом этапе тест еще не оформлен так как надо. Он только лишь воспроизводит баг. Далее я помещаю его в некую среду где я могу произвести отладку. В ней я, пользуясь отладчиком, разбираюсь что же не так в компиляторе и исправляю компилятор.
В процессе исправления я многократно перекомпилирую компилятор и прогоняю тот же тест. Если я вижу, что ошибка исправлена, то я запускаю двухпроходную сборку компилятора с прогоном вех тестов.
Обрати внимание, что новый тест при этом в них еще не входит.
Если все ОК и двухпроходная сборка с тестами прошли, то я создаю новый тест, помещаю его в состав тестов и еще раз прогоняю тесты. В этот раз уже на двух палтформах (для дотнет 2+ и 4). Если и в этот раз все ОК, то изменения комитятся в репозиторий. Если возникли проблемы, то начинается дальнейшее разбирательство.
Я не железный, по этому иногда халтурю. Но любое отступление от данного процесса чревато проблемами, которые периодически и появляются. И это подталкивает к более четкому соблюдению процесса.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
А зачем вы пишете такие тесты, от которых пользы ровно ноль?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Здравствуйте, DorfDepp, Вы писали:
DD>>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
ГВ>А зачем вы пишете такие тесты, от которых пользы ровно ноль?
Я вообще никакие не пишу. Зачем фигней страдать? И так работы полно без этого интеллектуального онанизма.
Здравствуйте, DorfDepp, Вы писали:
DD>>>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
ГВ>>А зачем вы пишете такие тесты, от которых пользы ровно ноль?
DD>Я вообще никакие не пишу. Зачем фигней страдать? И так работы полно без этого интеллектуального онанизма.
Может, потому, что ты их и не пишешь, у тебя работы полно? "Чего там думать, трясти надо!" (tm)
Здравствуйте, VladD2, Вы писали:
VD>Зачем писать синтетический тест тестирующий отдельную функцию когда можно написать программу компиляция и запуск которой проверит эту функцию в сто раз лучше?
[...]
В твоём сообщении я со всем согласен, кроме собственно примера с компилятором. Для тебя понятие "компилятор" имеет сильно другой смысл, чем ожидался мне и многим другим.
Для тебя образец понятия компилятора — это компилятор Nemerle, у которого на входе названный язык, а на выходе псевдокод машины .NET. У тебя корректность результата — фактически единственный чёткий фактор, а логика работы проста и понятна.
Когда же я представляю себе что-то под словом "компилятор", мне скорее всего вспоминается gcc. Ты ещё как-то можешь проверить корректность компиляции сравнением вывода программы с ожидаемым; более того, существенная часть тестов это и делает. Но, например, проверять так оптимизации просто нереально — они не дают тебе однозначно проверяемого вывода. Результат работы оптимизатора неустойчив (очень похожие версии после лёгких корректировок коэффициентов могут дать совершенно разный выходной код), вариантов входных данных безумное количество. Тут сравнивать вывод с ожидаемым методически некорректно — на простых примерах проблемы не проявляются, а сложные имеют слишком много степеней свободы. Устойчивый смысл возникает только у статистики на большом количестве примеров. В такой системе можно тестировать только отдельные мелкие подсистемы, а в этом случае тесты или являются юнит-тестами, или очень похожи на них.
Здравствуйте, DorfDepp, Вы писали:
ГВ>>А зачем вы пишете такие тесты, от которых пользы ровно ноль? DD>Я вообще никакие не пишу. Зачем фигней страдать? И так работы полно без этого интеллектуального онанизма.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, netch80, Вы писали:
N>В твоём сообщении я со всем согласен, кроме собственно примера с компилятором. Для тебя понятие "компилятор" имеет сильно другой смысл, чем ожидался мне и многим другим.
Так может проблема в твоем не верном восприятии этого понятия?
N>Для тебя образец понятия компилятора — это компилятор Nemerle, у которого на входе названный язык, а на выходе псевдокод машины .NET. У тебя корректность результата — фактически единственный чёткий фактор, а логика работы проста и понятна.
Ну, да. Я работаю непосредственно с Nemerle. Но все что ты сказал справедливо для любого компилятора (точнее даже, транслятора). Ну, разве что за исключением "логика работы проста и понятна". Как раз логика работы сложна и не поддается восприятием одним человеком в принципе (хотя ее всего пару метров).
N>Когда же я представляю себе что-то под словом "компилятор", мне скорее всего вспоминается gcc. Ты ещё как-то можешь проверить корректность компиляции сравнением вывода программы с ожидаемым; более того, существенная часть тестов это и делает. Но, например, проверять так оптимизации просто нереально — они не дают тебе однозначно проверяемого вывода.
Ну, почему же? В проле реально. Делая оптимизацию нужно проверять две вещи:
1. Компилятор выдает код семантически эквивалентный оному генерированному без оптимизации.
2. Оптимизация ускоряет некоторые юзкейсы.
Это тестами делается на раз.
Возможно, еще, проверить, что оптимизация генерирует некоторые инструкции. Тут систему тестирования нужно расширить, чтобы она декомпилировала исходник с и искала в нем некоторый паттерн.
Плюс оптимизатор — это на самом деле отдельный модуль. Его можно тестировать отдельно с помощью специальной утилиты.
N>Результат работы оптимизатора неустойчив (очень похожие версии после лёгких корректировок коэффициентов могут дать совершенно разный выходной код),
Та же фигня с любым компилятором. От того, например, в нашей тестовой утилите можно задавать опции компилятора в виде специального комментария-DSL-я. Например, вот как включается оптимизация.
N> вариантов входных данных безумное количество.
Входных данных для любого компилятора можно неограниченно нагенерировать.
Но для сочетания исходник + опции результат должен быть один и тот же. Компилятор не может быть генератором случайных чисел. Это всегда чистая функция.
N> Тут сравнивать вывод с ожидаемым методически некорректно — на простых примерах проблемы не проявляются, а сложные имеют слишком много степеней свободы.
Выдумываешь. Всегда можно написать пример демонстрирующий проблему. Так баги в компиляторах и ловятся.
N> Устойчивый смысл возникает только у статистики на большом количестве примеров. В такой системе можно тестировать только отдельные мелкие подсистемы,
Использование статистики для отладки? Это ты здорово придумал. Еще генератор случайных чисел для этого здорово использовать!
N>а в этом случае тесты или являются юнит-тестами, или очень похожи на них.
Под определение юнит-тестов — это не проходит. Тестируются не отдельные методы, а случаи использования. Это специализированная тестовая система. По уму для любого софта надо придумывать что-то подобное.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, DorfDepp, Вы писали:
DD>>>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль? ГВ>>А зачем вы пишете такие тесты, от которых пользы ровно ноль? DD>Я вообще никакие не пишу. Зачем фигней страдать? И так работы полно без этого интеллектуального онанизма.
Если ты действительно так считаешь, то ты круто ошибаешься. Собственно, не имеет значения, как называть "проверочные программы" — юнит-тестами, функциональными тестами, интеграционными, модульными, ещё как-то, суть не в названии (оно относится только к характеристикам охватываемой предметной области). Суть в том, что такими программами решаются задачи автоматизации контроля функционирования продукта. Я не спорю, от них можно подчас отказаться полностью, но именно что "подчас".
В твоём случае, похоже, кто-то увлёкся тестами отдельных кусочков, не занявшись плотно тестами композиций (сознательно не употребляю общепринятую терминологию). Такое может случиться по уйме различных причин: от неопытности до формализма и пренебрежения автоматическими проверками. И ты сейчас обобщаешь на весьма шатком основании, что, мол, юнит-тесты вообще нафиг не нужны. Если за какой-нибудь самопальной бухгалтерией придётся всё пересчитывать на счётах, это же не означает, что программисты вообще не нужны, правильно? Ну так и не опускайся до такого же уровня.
Собственно, в твоём вопросе заключена часть ответа: если программа не решает той задачи, для которой она, вроде бы, предназначена — нужно изменить программу. То есть если вы ждёте, что тесты обеспечат проверку работоспособности комбинации модулей, то нужно просто написать соответствующие тесты. Ну а если их нет, то ясное дело — проверяться комбинирование не будет. А как назвать эти тесты — юнит-, хрюнит-, грюнит-... Да хоть груздём назови, лишь бы вы у себя понимали, что как названо. Не нужно зацикливаться на глубоком смысле названий: если некоторый тест можно написать в рамках соглашений какой-то тестовой инфраструктуры (CppUnit, JUnit, NUnit, легионы их), но он по каким-то критериям не подходит под определение юнит-тест — это проблема самого определения, но никак не того, кто пишет тесты. И уж точно это не причина отказываться от самого тестирования, если имеется необходимость оного.
А безголовый подход (читай, без ясного понимания "зачем" — т.е. без ясной постановки задачи в контексте ясных требований) — вредит всегда и всем. Это справедливо не только для программирования.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, VladD2, Вы писали:
N>> Устойчивый смысл возникает только у статистики на большом количестве примеров. В такой системе можно тестировать только отдельные мелкие подсистемы, VD>Использование статистики для отладки? Это ты здорово придумал. Еще генератор случайных чисел для этого здорово использовать! :)
А что тебя в этом удивляет? Совершенно нормальный метод — случайность в выборе конкретных значений входных данных при условии 1) знания, что должно получиться в результате, с необходимой точностью и 2) в случае ошибки теста, тестовая среда сохраняет конкретные входные данные, чтобы дать человеку на проверку. Желательно также чтобы 3) тестовая среда могла, найдя так случайным образом пример нарушения теста, редуцировать его до предельно простого (не для всех видов тестов, только для поведенческих или других, где в принципе можно говорить о последовательном упрощении задачи, но, например, PropEr это умеет).
Да, это может быть непривычно в том смысле, что вроде бы в уже проверенном находится новая проблема, ну так всё равно ты их находишь раньше пользователя.
Конкретное применение случайных данных будет зависеть от задачи. Для компилятора это может быть, например, выход кодогенератора, у которого примерно известен результат, но в зависимости от ответов ГСЧ создаётся разное количество функций, перераспределяется код между ними, рисуются циклы и связи выполнения, и так далее.
И статистика — тоже важный показатель, если известно, что какое-то воздействие должно статистически улучшать результат.
N>> Тут сравнивать вывод с ожидаемым методически некорректно — на простых примерах проблемы не проявляются, а сложные имеют слишком много степеней свободы. VD>Выдумываешь. Всегда можно написать пример демонстрирующий проблему. Так баги в компиляторах и ловятся.
Постфактум — да, можно. Можно также запланированно тратить некоторую часть времени на придумывание "а какие тут ещё могут быть подводные камни и как их найти". Но в большинстве случаев примеры будут уже из практически достигнутого (или у пользователя, или у тестировщиков).
N>>Когда же я представляю себе что-то под словом "компилятор", мне скорее всего вспоминается gcc. Ты ещё как-то можешь проверить корректность компиляции сравнением вывода программы с ожидаемым; более того, существенная часть тестов это и делает. Но, например, проверять так оптимизации просто нереально — они не дают тебе однозначно проверяемого вывода. VD>Ну, почему же? В проле реально.
Где, простите?
VD> Делая оптимизацию нужно проверять две вещи: VD>1. Компилятор выдает код семантически эквивалентный оному генерированному без оптимизации. VD>2. Оптимизация ускоряет некоторые юзкейсы. VD>Это тестами делается на раз.
Ты так выловишь только очень простые юзкейсы. Конечно, легко проверить, что код вида int f(int x) { int a = x; return a; } будет без оптимизации сделан в виде снятия значения со стека, укладки его туда снова и затем снятия в регистр, а в случае оптимизации вместо трёх операций будет одна. Но это вообще неинтересный случай. Интересный пойдёт, когда функции на входе в оптимизатор имеют размеры в тысячи команд (стандартная ситуация в случае inline функций, раскрытия шаблонов и макросов). Тут ты тестами типа "подать товарный поезд на вход и получить простыню, идентичную данной, на выходе" долетишь в лучшем случае до следующей минорной версии, в которой всё это превратится в тыкву, потому что из-за смены пары констант подсчёт оптимума пойдёт по другому пути, регистры сдвинутся и в выводе не окажется ни одной совпадающей команды.
N>>а в этом случае тесты или являются юнит-тестами, или очень похожи на них. VD>Под определение юнит-тестов — это не проходит. Тестируются не отдельные методы, а случаи использования.
Повторюсь, я не считаю границу между юнит-тестами и тестами более высокого порядка, которые проверяют поведение, насколько принципиальной; в принципе это всё тесты функционирования какой-то части полной системы, и от размера и сложности этой части зависит только объём тестов и работа по настройке среды тестирования.
VD> Это специализированная тестовая система. По уму для любого софта надо придумывать что-то подобное.
Здесь категорически согласен, в том случае, если эта тестовая система окупится в разработке.
Здравствуйте, Aikin, Вы писали:
AVK>>Не так — когда сам дизайн меняется. Юнит-тесты при этом большими кусками перестают выполнять свою работу. Всего лишь. Это, в свою очередь, удорожает рефакторинг. A>Вывод не верный. Хоть и похоже на правду. Давай я свою "правду" озвучу: A>Когда сам дизайн менятся, функциональность существующих классов переноситься в другие классы.
Заблуждение, притом сильное. Функциональность существующих классов запросто может быть убрана полностью или трансформирована так, что старые юнит-тесты будет проще выкинуть, чем отрефакторить.
A>Чем большее количество логики меняет свое местоположение тем проще ошибиться и сложнее убедиться в том, что мы ничего не сломали рефакторингом. Чтобы убедиться в правильности рефакторинга нужно делать его ооочень внимательно и осторожно. А после окончания нужно обязательно прогнать систему по основным сценариям. Выявятся баги (куда же без них). Чтобы локализовать баг и исправить причину приходится дебажить, иногда очень долго. Это, в свою очередь, удорожает рефакторинг.
Так "дорогие" рефакторинги-то как раз и интересны. С другой стороны, на них летит к чёртовой бабушке вся стройная картина test-first, unit-test everywhere и всякие похожие лозунги.
AVK>>>>Об обычных. Глубокий рефакторинг ломает конктракты — ломаются все связанные с этим тесты. A>>>Любой рефакторинг идет итеративно, небольшими шажками. AVK>>Не любой. A>Тогда удачи тебе в глубоких рефакторингах одним махом.
Не понимаю твоего сарказма: такое делают нередко и к тому же — удачно.
A>>>При "очень глубоком рефакторинге" классы вместе с тестами проще выкинуть, чем изменять. AVK>>Классы — нет, не проще. Тесты — да, проще. О чем и речь. A>Юнит-тесты всегда значительно проще чем логика класса. Иначе в них практического смысла нет. A>Если же тест сложнее класса, то (на это должна быть причина) класс этот отвечает за критическую функциональность.
Как-то многовато у тебя выводится за рамки рассмотрения: глубокий рефакторинг, критическая функциональность, большие изменения дизайна. Такое ощущение, что твои высказывания на самом деле касаются программ, работающих, в основном, с чужим сложным кодом, функциональность которых не слишком "критична", а шаг вправо, шаг влево — и там уже "Дорого!" или "Критично!" Тогда как на практике нужно писать и критически важный код, и глубоко рефакторить, ну и много чего ещё.
Хм. Или это у меня одного такое впечатление?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Aikin, Вы писали:
AVK>>>>Когда сильно ковыряешь, unit-тесты очень плохи, так как висят свинцовой гирей и мешают проводить глубокий рефакторинг. А толку от них никакого, так как они пачками становятся просто неактуальными на новом дизайне. N>>>И что с того? AVK>>Дополнительная работа. A>А дебагинг очень нетривиального бага внесенного рефакторингом без тестов это не доп. работа? А работа QA который нашел этот баг? А время и головную заказчика, который не смог сделать то что ему нужно или еще хуже сделал то, что ему не нужно ты учитываешь?
Ну не выдумывай страшилок. Глубокая переработка подчиняется всем тем же правилам, что и любая другая разработка — вероятность сделать нетривиальную ошибку, в общем-то, одинакова в обоих случаях и зависит не от "глубины рефакторинга", а, скорее, от самого разработчика. Короче говоря, тут уже совсем другие факторы начинают играть.
Pzz>>>>>А иначе придется еще и тесты ковырять, не только класс. AVK>>>>Вот именно. N>>>И ничего в этом плохого нет. Если тест сломался, значит, его зацепило.
AVK>>Вопрос лишь в адекватности этого зацепления. В большинстве случаев, при сильном рефакторинге, я и так в курсе, какие тесты и почему сломаются до их запуска. Т.е. их срабатывание не приносит никакой пользы, и никто их не чинит, их либо основательно переписывать надо сразу, до запуска, либо вообще выкидывать, если тестируемый ими контракт изменился до неузнаваемости. A>Ценность тестов при рефакторинге не только в том, чтобы указать что именно ты сломал, но и в том, чтобы в конце ты все "вернул на место". Т.е. оставил логику в том же состоянии что и до рефакторинга. Это раз.
Это прямо конфликтует с необходимостью выбросить часть тестов после рефакторинга. То есть получается так, что в некоторых (и достаточно частых) случаях такая теория не применима.
A>Второй момент заключается в том, что хоть ты и предполагаешь какие именно тесты упадут (не будем говорить про изменение контрактов, когда компилятор может указать на ошибки), не не можешь быть 100% уверен, что не упадет что-то еще или что некоторые тесты изменения не затронут.
Так если контракт поменялся, то юнит-тесты не соответствующие новому контракту могут показывать что угодно — это только говорит о необходимости их переписать.
A>Опять же мы не рассматриваем очевидные случаи. Для них тестов (юнит) не будет.
Ну вот, снова ты отрезаешь кусок от проблемной области. Ты бы уж сразу её обрисовал, что ли. А то, куда ни кинь — по твоим словам выходит либо тривиальный случай, либо какое-то критическое ограничение, которое "не трогай", либо вообще не пойми, что.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Собственно, в твоём вопросе заключена часть ответа: если программа не решает той задачи, для которой она, вроде бы, предназначена — нужно изменить программу. То есть если вы ждёте, что тесты обеспечат проверку работоспособности комбинации модулей, то нужно просто написать соответствующие тесты. Ну а если их нет, то ясное дело — проверяться комбинирование не будет.
Проблема в том, что на большинстве реальных задач написание таких тестов занимает по времени больше, чем само решение задач. При этом при рефакторинге эти тесты все равно отваливаются, из-за изменений как архитектуры, так и требований. Именно по этой причине написание тестов в большинстве случаев это впустую потраченное время.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Если ты действительно так считаешь, то ты круто ошибаешься. Собственно, не имеет значения, как называть "проверочные программы" — юнит-тестами, функциональными тестами, интеграционными, модульными, ещё как-то, суть не в названии (оно относится только к характеристикам охватываемой предметной области). Суть в том, что такими программами решаются задачи автоматизации контроля функционирования продукта. Я не спорю, от них можно подчас отказаться полностью, но именно что "подчас".
ГВ>В твоём случае, похоже, кто-то увлёкся тестами отдельных кусочков, не занявшись плотно тестами композиций (сознательно не употребляю общепринятую терминологию). Такое может случиться по уйме различных причин: от неопытности до формализма и пренебрежения автоматическими проверками. И ты сейчас обобщаешь на весьма шатком основании, что, мол, юнит-тесты вообще нафиг не нужны. Если за какой-нибудь самопальной бухгалтерией придётся всё пересчитывать на счётах, это же не означает, что программисты вообще не нужны, правильно? Ну так и не опускайся до такого же уровня.
ГВ>Собственно, в твоём вопросе заключена часть ответа: если программа не решает той задачи, для которой она, вроде бы, предназначена — нужно изменить программу. То есть если вы ждёте, что тесты обеспечат проверку работоспособности комбинации модулей, то нужно просто написать соответствующие тесты. Ну а если их нет, то ясное дело — проверяться комбинирование не будет. А как назвать эти тесты — юнит-, хрюнит-, грюнит-... Да хоть груздём назови, лишь бы вы у себя понимали, что как названо. Не нужно зацикливаться на глубоком смысле названий: если некоторый тест можно написать в рамках соглашений какой-то тестовой инфраструктуры (CppUnit, JUnit, NUnit, легионы их), но он по каким-то критериям не подходит под определение юнит-тест — это проблема самого определения, но никак не того, кто пишет тесты. И уж точно это не причина отказываться от самого тестирования, если имеется необходимость оного.
ГВ>А безголовый подход (читай, без ясного понимания "зачем" — т.е. без ясной постановки задачи в контексте ясных требований) — вредит всегда и всем. Это справедливо не только для программирования.
Отличный ответ. Вот в таком виде я вопрос вполне поддержу.
Здравствуйте, netch80, Вы писали:
DD>>Я вообще никакие не пишу. Зачем фигней страдать? И так работы полно без этого интеллектуального онанизма.
N>Может, потому, что ты их и не пишешь, у тебя работы полно? "Чего там думать, трясти надо!" (tm)
Работы у меня полно, но работаю я очень аккуратно, поэтому неожиданно ничего не падает. Может, раз в год от силы, или того реже.
Здравствуйте, DorfDepp, Вы писали:
ГВ>>А безголовый подход (читай, без ясного понимания "зачем" — т.е. без ясной постановки задачи в контексте ясных требований) — вредит всегда и всем. Это справедливо не только для программирования.
DD>Отличный ответ. Вот в таком виде я вопрос вполне поддержу.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Undying, Вы писали:
ГВ>>Собственно, в твоём вопросе заключена часть ответа: если программа не решает той задачи, для которой она, вроде бы, предназначена — нужно изменить программу. То есть если вы ждёте, что тесты обеспечат проверку работоспособности комбинации модулей, то нужно просто написать соответствующие тесты. Ну а если их нет, то ясное дело — проверяться комбинирование не будет.
U>Проблема в том, что на большинстве реальных задач написание таких тестов занимает по времени больше, чем само решение задач. При этом при рефакторинге эти тесты все равно отваливаются, из-за изменений как архитектуры, так и требований. Именно по этой причине написание тестов в большинстве случаев это впустую потраченное время.
Я бы не стал обобщать, большинство случаев — оно у каждого своё. Скорее, здесь имеет значение общий подход к тестированию. Если мы пишем тесты под влиянием какого-нибудь милого голубоглазого лозунга, вроде: "каждый метод непременно должен сопровождаться юнит-тестом" или "сначала непременно пиши тесты" — то да, есть приличный шанс нарваться на то, что большую часть времени мы будем тратить на переписывание тестов. Ну а если мы пишем тесты ради того, чтобы решить какие-то, вполне определённые задачи — то всё в порядке.
Например, мы хотим зафиксировать контракт и предостеречь сами себя от случайного его изменения. Отлично, пишем набор тестов. И после этого нет никакого смысла жаловаться, если вдруг мы всё же решили сильно поменять контракт — на момент принятия решения о покрытии тестами мы были на 101% уверены в его стабильности.
Или, скажем, хотим, чтобы вот этот набор функций был очень надёжным и протестирован в мыслимых и немыслимых условиях, и плевать, что когда-то он поменяется — снова пишем набор тестов. И снова, даже если этот набор функций раньше или позже оказался выкинут полностью — нет смысла жаловаться, поскольку тестами мы решали актуальную в какой-то момент задачу.
Ещё тесты можно писать банально ради отладки — тестовая инфраструктура зачастую вполне подходит для этого. тут вообще жаловаться не на что и незачем.
Суть в том, что мы сначала определяем задачу, имеющую практический смысл, ради решения которой пишется тест, и только потом, после того, как мы её сформулировали, мы пишем тесты. Если окажется, что мы сплоховали во время выделения задачи — тесты тут не при чём, это лечится совсем с другого конца.
Но повторюсь, если тесты пишут только потому, что так сказал Гуру Методологии — всё, пиши пропало. Будет срач, вопли, недоумённые восклицания и, что самое забавное — тесты плавно выйдут из эксплуатации одновременно с изменением глубины веры в заветы Великого. Короче, будет всё, как всегда в нашем зоопарке.
Premature testing is a root of evil, так сказать. Ключевое слово, как всегда — Premature.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, DorfDepp, Вы писали:
DD>...если они на практике ничего не ловят. Каждый метод будет прекрасно работать в изоляции, а упадет обязательно на нестыковке компонент, из-за старого формата данных в базе и т.п. Ради чего надо писать 10 минут метод и потом час-два к нему тесты, если пользы ровно ноль?
Присоединяюсь ко всем, кто высказался в пользу юнит-тестов.
Могу добавить только, что вот прямо вчера мне удалось спасти релиз от весьма неочевидного бага именно благодаря отвалившемуся юнит-тесту.
Пример прямо из жизни, свежее не бывает.
Здравствуйте, Геннадий Васильев, Вы писали:
A>>Опять же мы не рассматриваем очевидные случаи. Для них тестов (юнит) не будет.
ГВ>Ну вот, снова ты отрезаешь кусок от проблемной области. Ты бы уж сразу её обрисовал, что ли. А то, куда ни кинь — по твоим словам выходит либо тривиальный случай, либо какое-то критическое ограничение, которое "не трогай", либо вообще не пойми, что. Re: Нафига нужны юнит-тесты?
Здравствуйте, VladD2, Вы писали:
VD>Автоматизировать обезьянью работу.
Спасибо Кэп! Давай я разверну, а ты потом прокоментируешь.
Ну и чтобы было интересно, то вот часть моей "обезьяньей работы":
Приходит запрос от Бизнеса в виде: "большая кнопка должна делать Чпок". Ты понимаешь, что это может означать все что угодно. Выбираешь 2-3 наиболее вероятных варианта. Начинаешь слать письма. После переписки задача понятна. Начинаешь анализировать. Вылезли нюансы/проблемы/ограничения. Опять переписка. Видишь, что переписки мало -- звонишь. В параллель ты делаешь этот функционал. Показываешь, исправляешь. и т.д.
Как я ненавижу это, но это моя работа. Внимание вопрос: как это автоматизировать?
А еще есть обезьянья работа по переводу спецификаций и требований в код. Написание документации. И проч.
СУВ, Aikin
ЗЫ Обезьянья работа озвученая АндреемВК мне нравится -- люблю рефакторить. Жаль делаю это не так часто.
Здравствуйте, Aikin, Вы писали:
A>Ну и чтобы было интересно, то вот часть моей "обезьяньей работы": A>Приходит запрос от Бизнеса в виде: "большая кнопка должна делать Чпок". Ты понимаешь, что это может означать все что угодно. Выбираешь 2-3 наиболее вероятных варианта. Начинаешь слать письма. После переписки задача понятна. Начинаешь анализировать. Вылезли нюансы/проблемы/ограничения. Опять переписка. Видишь, что переписки мало -- звонишь. В параллель ты делаешь этот функционал. Показываешь, исправляешь. и т.д. A>Как я ненавижу это, но это моя работа. Внимание вопрос: как это автоматизировать?
Это не обезьянья работа. Это даже не работа для программиста. Тут хорошо бы найти подходящего специалиста которому бы эта работа нравилась, и который выполнял бы ее творчески. Вот от что-нибудь придумал бы.
A>А еще есть обезьянья работа по переводу спецификаций и требований в код. Написание документации. И проч.
А вот это как раз автоматизировать можно. Нужно формализовать спецификации. Придумать для них ДСЛ (возможно не один). Написать генераторы кода которые превратят спеки в рабочий код.
Структуру документации тоже таким образом можно создать. Саму документацию придется писать вручную (ИИ пока нет). Но это опять же не обязьянья работа. И опять работа не для программиста, а для тех.писателя.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
A>>А еще есть обезьянья работа по переводу спецификаций и требований в код. Написание документации. И проч.
VD>А вот это как раз автоматизировать можно. Нужно формализовать спецификации. Придумать для них ДСЛ (возможно не один). Написать генераторы кода которые превратят спеки в рабочий код.
Забыл глумиться. Сори.
Для вас скорее всего это такой рокет-сайнс, что вы даже не считаете это реальным. Но это более чем реально, если не упираться рогом в ООП или ФП.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>А вот это как раз автоматизировать можно. Нужно формализовать спецификации. Придумать для них ДСЛ (возможно не один). Написать генераторы кода которые превратят спеки в рабочий код. VD>Структуру документации тоже таким образом можно создать. Саму документацию придется писать вручную (ИИ пока нет). Но это опять же не обязьянья работа. И опять работа не для программиста, а для тех.писателя.
вот мне интересно, а как ты себе это представляешь?
все спецификации с которыми мне доводилось работать выглядели примерно так: значение в третьем столбце таблицы должно вычислятся по формуле .. и дальше математическая выкладка на полстраницы.
вот как к примеру ты формализовал бы эту спецификацию, чтобы избавится от рутины?
Здравствуйте, genre, Вы писали:
G>вот мне интересно, а как ты себе это представляешь?
G>все спецификации с которыми мне доводилось работать выглядели примерно так: значение в третьем столбце таблицы должно вычислятся по формуле .. и дальше математическая выкладка на полстраницы.
G>вот как к примеру ты формализовал бы эту спецификацию, чтобы избавится от рутины?
Любому полю всегда можно дать краткое и развернутое описание. Если заставлять их заполнять и контролировать их качество, то вполне можно будет генерировать краткую справку.
В общем, конечно нужно смотреть по обстоятельством. Но пытаться автоматизировать нужно. Все стремится к хаосу (т.е. бардаку), а бардак автоматизировать нельзя (с). Так что нужно бороться с хаосом как получается. Формальные спецификации один из видов такой борьбы. Некоторые задачи можно полностью описать формальными спецификациями. Классический пример — парсеры ЯП. Грамматика — это и есть формальная спецификация.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Любому полю всегда можно дать краткое и развернутое описание. Если заставлять их заполнять и контролировать их качество, то вполне можно будет генерировать краткую справку.
не понял, а при чем тут справка?
VD>В общем, конечно нужно смотреть по обстоятельством. Но пытаться автоматизировать нужно. Все стремится к хаосу (т.е. бардаку), а бардак автоматизировать нельзя (с). Так что нужно бороться с хаосом как получается. Формальные спецификации один из видов такой борьбы. Некоторые задачи можно полностью описать формальными спецификациями. Классический пример — парсеры ЯП. Грамматика — это и есть формальная спецификация.
Ага, то есть переложим рутину на плечи аналитиков? ведь запись требований на формальном языке это и есть программирование
кроме того вызывает серьезные сомнения экономическая эффективность такого подхода.
Здравствуйте, genre, Вы писали:
G>не понял, а при чем тут справка?
Подставь нужный термин.
VD>>В общем, конечно нужно смотреть по обстоятельством. Но пытаться автоматизировать нужно. Все стремится к хаосу (т.е. бардаку), а бардак автоматизировать нельзя (с). Так что нужно бороться с хаосом как получается. Формальные спецификации один из видов такой борьбы. Некоторые задачи можно полностью описать формальными спецификациями. Классический пример — парсеры ЯП. Грамматика — это и есть формальная спецификация.
G>Ага, то есть переложим рутину на плечи аналитиков? ведь запись требований на формальном языке это и есть программирование
Ну, да. Это и есть программирование. Разница только в уровне кода. Если ты будешь долбить его на явах, сишарпах и прочей вижулбясите, то аналитик поглядит на это минут пять и пойдет заниматься чем-то другим.
Если же ты создашь для своего аналитика формальный, но высокоуровневый, язык, который будет оперировать понятиями предметной области, а не классами или функциями, то толковый аналитик сможет включиться в процесс программирования. Возможно, не один, а вместе с тобой, как экспертом в области программирования.
G>кроме того вызывает серьезные сомнения экономическая эффективность такого подхода.
А ты подумай. У тебя исчезает целый не формализованный слой. Код становится декларативным и высокоуровневым.
Единственная проблема этого подхода — нужно реализовать поддержку этого ДСЛ-я. Нужен компилятор и (желательно) поддержка в IDE. И тут становится очевидна другая проблема. Современные мэйнстрим-инструменты на это не рассчитаны. Поддержка ДСЛ-я средствами ООЯ или даже ФЯ — это слишком сложная задача. Так что если ты не видишь жизни за пределами мэйнстрима, то этот подход не для тебя.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, genre, Вы писали:
G>Здравствуйте, VladD2, Вы писали:
VD>>А вот это как раз автоматизировать можно. Нужно формализовать спецификации. Придумать для них ДСЛ (возможно не один). Написать генераторы кода которые превратят спеки в рабочий код. VD>>Структуру документации тоже таким образом можно создать. Саму документацию придется писать вручную (ИИ пока нет). Но это опять же не обязьянья работа. И опять работа не для программиста, а для тех.писателя.
G>вот мне интересно, а как ты себе это представляешь?
Здравствуйте, VladD2, Вы писали:
VD>Если же ты создашь для своего аналитика формальный, но высокоуровневый, язык, который будет оперировать понятиями предметной области, а не классами или функциями, то толковый аналитик сможет включиться в процесс программирования. Возможно, не один, а вместе с тобой, как экспертом в области программирования.
Что-то есть у меня сомнения по этому поводу и очень серьезные. Нет, звучит все крайне привлекательно, но в реальной жизни мне кажется это применимо к крайне узкому кругу задач.
Как это можно применить к например написанию какой-нибудь трехзвенной платформы, с сервером, бд и толстым клиентом я не очень понимаю.
Или даже проще. Допустим мы пишем почтовый клиент. Или даже просто простой текстовый редактор. Что тут можно описать адекватным дслем?
G>>кроме того вызывает серьезные сомнения экономическая эффективность такого подхода.
VD>А ты подумай. У тебя исчезает целый не формализованный слой. Код становится декларативным и высокоуровневым.
Ну вот например в тех областях с которыми мне доводилось работать предметная область настолько объемна, что затраты на создание подобного дсл выглядят сравнимыми с затратами на написание собственно кода.
Ну и куча сопутствующих проблем, например где найти аналитика, который не только эксперт в предметной области, но и готов учится писать на этом дсл?
VD>Единственная проблема этого подхода — нужно реализовать поддержку этого ДСЛ-я. Нужен компилятор и (желательно) поддержка в IDE. И тут становится очевидна другая проблема. Современные мэйнстрим-инструменты на это не рассчитаны. Поддержка ДСЛ-я средствами ООЯ или даже ФЯ — это слишком сложная задача. Так что если ты не видишь жизни за пределами мэйнстрима, то этот подход не для тебя
Ну для большинства коммерческих продуктов жизни за пределами мейнстрима действительно нет.
Здравствуйте, Abyx, Вы писали:
VD>>>А вот это как раз автоматизировать можно. Нужно формализовать спецификации. Придумать для них ДСЛ (возможно не один). Написать генераторы кода которые превратят спеки в рабочий код. VD>>>Структуру документации тоже таким образом можно создать. Саму документацию придется писать вручную (ИИ пока нет). Но это опять же не обязьянья работа. И опять работа не для программиста, а для тех.писателя.
G>>вот мне интересно, а как ты себе это представляешь?
A>есть же FitNesse, Cucumber
что-то я не могу найти на их сайтах внятного описания о том как оно работает для написания кода, а не для его тестирования.
Здравствуйте, genre, Вы писали:
G>Здравствуйте, Abyx, Вы писали:
VD>>>>А вот это как раз автоматизировать можно. Нужно формализовать спецификации. Придумать для них ДСЛ (возможно не один). Написать генераторы кода которые превратят спеки в рабочий код. VD>>>>Структуру документации тоже таким образом можно создать. Саму документацию придется писать вручную (ИИ пока нет). Но это опять же не обязьянья работа. И опять работа не для программиста, а для тех.писателя.
G>>>вот мне интересно, а как ты себе это представляешь?
A>>есть же FitNesse, Cucumber
G>что-то я не могу найти на их сайтах внятного описания о том как оно работает для написания кода, а не для его тестирования.
так же как и другие test-first техники. пишите тест, и он помогает во время написания кода
VD>>>>>А вот это как раз автоматизировать можно. Нужно формализовать спецификации. Придумать для них ДСЛ (возможно не один). Написать генераторы кода которые превратят спеки в рабочий код. VD>>>>>Структуру документации тоже таким образом можно создать. Саму документацию придется писать вручную (ИИ пока нет). Но это опять же не обязьянья работа. И опять работа не для программиста, а для тех.писателя.
G>>>>вот мне интересно, а как ты себе это представляешь? A>>>есть же FitNesse, Cucumber
G>>что-то я не могу найти на их сайтах внятного описания о том как оно работает для написания кода, а не для его тестирования. A>так же как и другие test-first техники. пишите тест, и он помогает во время написания кода