Как стать автором
Обновить

Комментарии 75

<font color="red">
я уже и забыл когда видел такое :)
Да, склероз вещь хорошая :)
У неверстальщиков такое сплошь и рядом. Я и сам часто исспользую.
Это бизнес-логика. :)
НЛО прилетело и опубликовало эту надпись здесь
Тогда уж .
С тех пор, как узнал про CSS, про font и color забыл напрочь, и вспоминаю только если где-то увижу.
Тогда уж так:
<div style="color:red;">


P.S. парсер съел в предыдущем комменте, каюсь.
Вы скорее не тестами занимаетесь в данном случае, а рефакторингом кода. Тесты все таки созданы для поиска ошибок.
Тесты — не для поиска ошибок. Для этого тоже, но прежде всего для другого — для тестирования поведения.
нет-нет-нет. В первую очередь тесты нужны для актуальной документации кода.
Если тест не работает — значит этот кусок документации неактуален.
+ 10000000 :)
Предлагаю вам переформулировать своё высказывание. «Тесты — для тестирования ...» — очень плохо звучит.
Ну как бэ TDD есть, где тесты служат скорее для написания кода, чем для «поиска ошибок».

А есть ваабще BDD где в тестах лежат требования и спецификации…

Так что «тесты» уже да-авно развились из средства «поиска ошибок» до удобного инструмента написания кода.
И там и там тесты используются для поиска ошибок.
Утверждение «тесты используются для поиска ошибок» значит что цель тестов — найти ошибку в существующем коде. Для TDD/BDD это не совсем является верно, так как кода еще нет.

Можно, конечно же, дойти до того, что отсутвие кода назвать ошибкой этого кода… но это уже алория какая-то, ведь тогда и ошибки не существует, раз кода нет :)

Мне кажется что намного более верно утверждение что тесты в BDD/TDD используются для написания кода и слежения за тем, чтоб он оставался верным (не содержал ошибок). Из этого утверждение видно, что «поиск ошибок» — вторичная функция тестов, и «поиска» как такового не происходит — скорее слежение за тем, чтоб они не появлялись.
Для TDD/BDD это не совсем является верно, так как кода еще нет.


Какая разница когда писать тесты? TDD это по сути процесс разработки в котором написание тестов идет в первую очередь, а код во вторую. Но назначение этих тестов банальное — поиск последующих ошибок в последующем коде. По идее разницы когда писать тесты и на чем их писать (хоть на бумажке) нет. Например я, проектируя калькулятор, знаю, что у меня будет функция Plus(int a, int b).

Не имея этой функции я уже могу писать тест, который будет проверять её корректность:

template class TestCalc
{

public:

void Plus(Calc calc, int a, int b)
{
ASSERT(calc.Plus(1, 2) == 3);
}

};

Заметь, у меня Тест проверяющий ошибку есть, а самого Калькулятора еще нет.

Можно, конечно же, дойти до того, что отсутвие кода назвать ошибкой этого кода… но это уже алория какая-то, ведь тогда и ошибки не существует, раз кода нет :)


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

TestCalc темплейт с параметром типа Calc. Строгий Хабр съел правильное объявление :)
Назначение тестов — фиксация факта отсутствия ошибок в коде (в рамках этих тестов, конечно). Локализация появившихся ошибок — вторичная функция тестов. В тру TDD она как бы и не нужна — красная полоса появляется после «атомарных» изменений (создали тест, проверяющий ещё не созданную функциональность, или сделали какое-то изменении, нарушившее созданную функциональность). Если склероз ещё не совсем поразил разработчика, а diff в VCS ещё работает, то место последнего изменения найти не сложно :)

К примеру, написали вы тест к калькулятору, как он поможет вам найти ошибку? Подскажет где именно вы ещё не написали сам калькулятор? :) И не поможет он найти вам ошибку в случае если по каким-то причинам объект класса Calc не создаётся — может вы ещё класс не написали, может написали, но поместили его не там, где транслятор его ищет, может опечатались в названии, может в конструкторе что-то намутили, а может в самом тесте. У вас будет только факт — есть ошибка, объект не создаётся.
Под поиском ошибок я имел ввиду именно фиксацию факта наличия ошибки, а в чем именно ошибка будет разбираться разработчик :)

Хотя мой пример, будет выдавать уже больше информации об ошибке, чем просто её наличие.
Чтобы данный тест хотябы скомпилировался, необходимо, чтобы параметром функции Plus был подан объект с интерфейсом Plus(a, b). После компиляции я уже знаю, что такой класс и объект у меня имеются. Далее если возникает ASSERT я понимаю, что функция Plus отработала неверно и начинаю разбираться с методом Plus объекта Calc, который попал в параметр.
Фиксация факта наличия или фиксация факта отсутствия почти одно и тоже, но всё же настаиваю, что основное назначение тестов фиксация факта отсутствия ошибок :)

Если бы ассерт писал только «error», без пояснений, вы же всё равно бы поняли в чём проблема, поскольку только что этот тест и «пустой» код к нему написали. Если написали в пятницу вечером, а в понедельник не помните, то взгляните на диффы и вспомните :)
По идее разницы когда писать тесты и на чем их писать (хоть на бумажке) нет.

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

Т.е. все-таки важно когда писать тесты.
Разницы нет для самого понятия Теста. Для TDD разница естественно есть. Мы не используем в проектах TDD и пишем тесты уже для давно существующего кода. Но задача у нас и у тех то пишет тесты в TDD общая — фиксация ошибок в коде, который будет меняться. Естественно писанина тестов влечет за собой также и «фиксацию» интерфейсов.
И вот тут как раз и проявляется разные цели написания теста.

В TDD и производных цель теста — написать правильный код. А при написании теста на существующий — зафиксировать/проверить поведение.

И именно на эту разницу я и обратил внимания комментируя утверждение «Тесты все таки созданы для поиска ошибок» (выделил «для» чтоб подчеркнуть, что в утверждении говорится о цели создания тестов). Подчеркнуть что цели тестов бывают разные в т.ч. тесты пишутся для написания кода.

Хотя тут я уже начинаю повторять что написанно выше про цель тестов.
Тут имеет смысл вспомнить, что ошибка может считаться ошибкой только тогда, когда есть спецификация. Нет спецификации — нет ошибки, т.к. «ошибочное» поведение ничему не противоречит, нигде не указано, как должно быть.
По-моему тесты должны описывать контракт.
И если не можете написать тестов — скорее всего, имеет смысл пересмотреть контракт.

Вот только создавать тесты в старом проекте, в котором об IoC и не слыхивали, довольно сложно, потому как тестируемый класс может лазить в базу, например. А возводить подобную инфраструктуру просто не хватает времени.
Толком не понял, что такое контракты, но как-то пишу тесты и код без них :) И как это «не можете написать тестов» при разработке с нуля с использованием TDD? Не можете описать, что код, который собираетесь написать, делает?
А кто сказал, что контроллеры и представления не содержат логику? И что ее не надо тестировать?

Наличие бизнес-логики или «знаний» (Domain Logic) — отнюдь не означает, что не нужно тестировать «логику приложения» (App Logic) и логику представлений (Presentation Logic).
Я просто сказал, что я часто слышал такие фразы.
Я не знаю, кто это сказал, да это и неважно. Ну хорошо, можно поменять фразу на «контроллеры ДОЛЖНЫ содержать логику» — суть не меняется.
то есть в слое «view», в котором по определению никакой логики быть не должно
по определению там должна быть логика представления, и у вас она осталась:
<c:if test="${account.inDebt}">
  <font color="red">Оплатите задолженность!</font>
</c:if>
По хорошему её тоже надо тестировать, но так как тестировать её сложно, а код тривиальный, то на это часто забивают. Проблемы начинаются когда код перестаёт быть тривиальным, причём мелкими шажками, а не скачкообразно — «внезапно» оказывается, что сложно даже понять, правильно ли отображается шаблон.

Вообще много путаницы из-за того, что логикой то называют бизнес-логику, то любую логику вообще, которой действительно не может не быть ни на одном уровне приложения. Есть код — есть у него логика :)
Думаю, стоит использовать более корректную терминологию.
Бизнес-логику, конечно, иначе уже не назовешь, прижилось.
А вот логику представления и логику работы с БД можно назвать иначе.

В случае view это, конечно, тоже логика, но в первую очередь это отображение. Поэтому имеет смысл называть это «правилами отображения». Более того, это согласуется с термином «правила диспетчеризации» (dispatching rules).

В случае БД это запросы и структурирование результата. Поэтому можно назвать это «запросами», как правило низкоуровневую работу с БД на уровне запросов так и называют.

В большом, если не подавляющем, числе веб-приложений, бизнес-логика и логика работы с БД очень тесно связаны друг с другом и, зачастую, находятся в одних классах (паттерн ActiveRecord например). Не говоря уж о случаях, когда бизнес-логика реализуется средствами БД (триггеры, хранимые процедуры и т. п.). А правила диспетчеризации — это как раз логика приложения.
НЛО прилетело и опубликовало эту надпись здесь
Еще можно добавить, что написание тестов помогает улучшить архитектуру, устраняя лишние привязки и повышая связность.
Эм…

«улучшить архитектуру» и «повышая связность» — это противоречие. Наверное, вы имели ввиду «уменьшая связанность».
Не путайте связность и связанность, большая связанность (связи между модулями) это плохо, большая связность (связи внутри модуля) — нет.
не понял почему второе — нет.
Ведь тогда просто напросто усложняется изменение этого модуля.
Да не сказал бы, например, вынося повторяющийся код из двух публичных методов в один приватный, мы увеличим связность (публичные методы будут зависеть от приватного), но избавимся от необходимости одновременно исправлять этот код в двух местах. Конечно, может сложиться ситуацияя, когда код надо будет или разделять опять на два куска, или делать его универсальным с каким-то оверхидом, но в целом, по-моему, такой код поддерживать проще, не смотря на большее число связей в нём. Малая связность — сигнал о том, что у модуля появилось несколько ответственностей. Почему я, например, не люблю паттерн ActiveRecord — в одном классе существуют две малосвязных функциональности — бизнес-логика и логика хранилища данных.
Думаю, имеет смысл расшифровать.

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

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

Но добавлю, что разделять на группы надо так, чтобы не увеличить связанность, иначе и смысла нет менять шило на мыло. Лучше иметь одну большую сильносвязную группу кода, чем две (и более) сильносвязанных поменьше. Собственно именно поэтому, по-моему, и введено два разных, но близких термина :)

А вообще на практике часто получается, что когда код группы растёт по объёму, то его связность уменьшается, и одновременно нарушается принцип единственной ответственности. Тут тоже надо разделить ответственность так, чтобы не сильно увеличивать связанность, или, хотя бы, быть готовым к её уменьшению при необходимости, то есть не сразу вводить DI, но писать код так, чтобы её легко можно было ввести.
Отправилось раньше…

Например, Dependecy Injection понижает связанность, а инкапсуляция повышает связность.
Тесты прежде всего помогают облегчить рефакторинг, а вот грамотный рефакторинг улучшает архитектуру. Если рефакторинг не грамотный, то тесты помогут ухудшить архитектуру :)
То, что тесты облегчают рефакторинг — это одна сторона. Но ведь чем выше у класса связность, тем проще его тестировать и наоборот. Т.о. написание большого количества тестов для класса с низкой связностью потребует его рефакторинг к принципу единой ответственности, что и приведет к улучшению архитектуры. Также лишние привязки в системе затруднят написание тестов, и чтобы можно было написать много тестов придется проводить рефакторинг к принципу инверсии зависимостей, что также положительным образом скажется на архитектуре.
НЛО прилетело и опубликовало эту надпись здесь
Хм… Я предпочитаю примитивные контроллеры (получили объект модели по ид и передали его в вид, получили ввод от пользователя и передали его в модель) и сложные модели (не только получение данных из хранилища, но и практически всю работу с ними, в идеале «raw» данные, полученные от DB, DBAL, ORM, вообще не должны, по моему, использоваться в контроллере, а тем более в виде).
НЛО прилетело и опубликовало эту надпись здесь
ну собственно «сложные модели» для того и предназначены, чтобы бизнес-логика обслуживалась в одном месте, а не была размазана по нескольким контроллерам.
Вот в одном месте — модели — она и обслуживается (реализуется), контроллеры только создают модель, изменяют её состояние «сообщениями» о действиях пользователя, и передают её в вид, который получает состояние и отображает его. Модель не зависит ни от способа хранения данных (может в одной базе, может в другой, может в файле или памяти, а может удалённый сервис), ни от интерфейса управления (одинаково будет работать как GUI/CLI — приложение, как веб-сайт или сетевой сервис), ни от способа вывода состояния (хоть в консоль, хоть в GUI-контролы, хоть в html/xml/json, хоть в закрытый бинарный формат). Контроллер знает как сообщить модели о том, что пользователь хочет и каким конкретным видом передать пользователю результат, вид знает как отобразить результат для пользователя.

Понадобится нам изменить поведение — изменяем только модель. Понадобится изменить отображение — изменяем только вид. Понадобится сообщить модели о каком-то (уже реализованном в ней) желании пользователя — изменяем (или создаём) контроллер.
НЛО прилетело и опубликовало эту надпись здесь
И что некоторые из них не вызывают внутреннего отторжения :)
НЛО прилетело и опубликовало эту надпись здесь
Полностью автора поддерживаю! В том что касается высказывания «логика для того, сего..., бизнес логика и пр.». помню, когда начинал — такие вещи меня в ступор вгоняли, и никто на пальцах объяснить не мог. млин, нахватаются забугровой терминологии, как сказать по нашему не могут, и умничают перлами типа: «код асайнит бизнес логику которая в свою очередь релейтит вью»!
я понял понятие «бизнес-логика» как «эта нелогичная хрень для удовлетворения потребностей пользователя/заказчика»
Даже зная перевод этих слов, сложно быстро врубиться что за кашу вам сказали. Таких умников всегда прошу доступно и популярно обьяснить по русски.
Как говорит Джоэл, таких умников даже на работу брать не стоит.
Напомнило мне комментарий, который я недавно нашел в коде одного проекта — «Создает дефиницию формы».
Идея верная, но по-моему, того же результата можно добиться, просто посмотрев на код контроллера и решив, что можно оттуда перенести в более подходящее место.
Может, и можно.
Ну и хорошо. Пусть будет много возможностей.
Ну не знаю… я всегда считал что «бизнес-логика» приложения — это код который описывает __процессы__ предметной области, каким бы этот код ни был. ИМХО вполне нормальное определение, по крайней мере лучше чем «всё, что вам хотелось хоть раз откомментировать».
НЛО прилетело и опубликовало эту надпись здесь
(о юнит-тестах) не обязательно добиваться стопроцентного покрытия кода тестами, достаточно тестировать лишь логику.

В тру TDD оттестировано должно быть всё, что ещё не оттестировано. Причиной появления (удаления, изменения) любой строчки кода в тестируемом коде должен быть или не сработавший тест, или рефакторинг, не изменяющий «чёрного ящика».

На практике всё, конечно, сложнее и на тестирование многих вещей забивают или в связи со сложностью самого тестирования (прежде всего внешние интерфейсы системы, включая пользовательские), или в связи с тривиальностью кода (правда иногда, тривиальность оказывается кажущейся, что приводит к ситуациям «тесты проходят, а код не работает»). Нередко тесты избыточны, например, тестируют не свой код, а уже оттестированные кем-то модули, библиотеки, фреймворки и из-за этой избыточности существенно замедляется скорость разработки (особенно учитывая, что такие тесты чаще всего сложны, так как относятся к внешним интерфейсам), в результате чего на тестирование забивают вообще или тестируют только тривиальные вещи.
Оказалось, что эти IF'ы действительно были в коде, причём не где-нибудь, а в JSP-файле

Ого, у вас что только в виде проверялось наличие денег у клиента, круто!
Именно так.
That's the point.
> Возможно, ответить на этот вопрос проще, если вместо слова «логика» использовать слово «знание».

Возможно, ответить на этот вопрос будет проще, если почитать профильную литературу. Для совсем ленивых — вполне достойное определение есть в википедии:
«Бизнес-логика — в разработке информационных систем — совокупность правил, принципов, зависимостей поведения объектов предметной области (области человеческой деятельности, которую система поддерживает). Иначе можно сказать, что бизнес-логика — это реализация правил и ограничений автоматизируемых операций. Является синонимом термина «логика предметной области» „
(рус / eng).
Просто в определённых кругах (когда лежит «dev environment», естественно. и только тогда!) вместо чтения достойной литературы [irony](или, на худой конец, улучшения демографической ситуации)[/irony] принято для поиска эфемерной бизнес-логики в дебрях синглтоно-шаблонно-задекорированных джунглей писать модульные тесты.

[irony]Они повышают связность.[/irony]

Мрак.
> «Бизнес-логика — в разработке информационных систем — совокупность правил, принципов, зависимостей поведения объектов предметной области (области человеческой деятельности, которую система поддерживает). Иначе можно сказать, что бизнес-логика — это реализация правил и ограничений автоматизируемых операций. Является синонимом термина «логика предметной области» „

Все бы хорошо, и может быть, даже, в Вашем случае это определение вполне соответствует действительности.

Например, в моем случае «бизнес-логика» не более чем бессмыслица (buzz-word). Например, когда читаю в документации «correspondence types are grouped into groups» (нууу… не совсем так, это я для иллюстрации структуры предложения), ни о каком знании, а тем более логике и речи быть не может — разве что о шаманстве.
На мой взгляд вы несколько не верно поняли сути разделения на слои.
Для чего вообще придумали такое разделение? Чтобы в команде разработчиков можно было более-менее безболезненно поделить зоны ответственности — если тот, кто занимается внешним видом захотел что-то поменять в интерфейсе пользователя — он должен иметь возможность сделать это самостоятельно, не обращаясь к другим членам команды. Соответственно, если ответственный за бизнес-логику решил оптимизировать скорость выполнения определенных процедур — ему не нужно вмешиваться в пользовательский интерфейс.
Поэтому верно сделан такой рефакторинг, или нет — вопрос спорный. Если помимо странички отображения данных у isInDebt() есть другие вызовы и применения — то да, все сделано в нужном русле. Но если данная страничка — это единственное использование этого метода — то лучше было оставить как есть, так как изначально при программировании представления вы не вмешивались в другой слой, а именно в класс Account и использовали только один его метод — getAmount(). После рефакторинга — таких методов стало уже два, а «в идеале» — целых 4!!! Где теперь искать ошибку, если вместо красного рисуется зеленый? В первоначальном варианте — в одном месте. В «идеальном» — в пяти.
НЛО прилетело и опубликовало эту надпись здесь
А самое ужасное в том, сколько из тех, кто пишет финансовые приложения (sic!) этого еще не поняли!
НЛО прилетело и опубликовало эту надпись здесь
Так статья про бизнес-логику и архитектуру, или про юнит-тесты?
Заголовок стати не верный, в нем нет ни единого упоминания про TDD и его влияния на архитектуру.
Мое впечетление — автор хотел написать про бизнес-логику, но про что-либо иное окромя TDD он думать не может (автор — робот?)…
Иногда бизнес-логикой приложения почему-то начинают называть то, чего хочет видеть клиент, т.е. в первую очередь правила отображения. Из-за этого, в основном, и идет путаница.
Я за то, чтобы требования клиента так и называть требованиями, а не бизнес-логикой. Тогда все будет на своих местах.
Если честно, то бред какой-то… Что такое логика в чистом виде вам ответит теория Конечного Автомата и Машина Тьюринга, например. Но говорить, что IF это логика или не логика, бред конечно, конъюнкции дизъюнкции — это логика в чистом виде в парадигме архитектуры фон Неймана.
Ну и без тестов было ясно, что сущность счет спроектирована стремно. Сложно вам, если такие вещи не очевидны.
Надо понимать, что для написания статьи обычно берутся упрощённые примеры. Из них выкинуты другие сложности и второстепенные детали, иначе статья получилась бы слишком сложной и запутанной. Поэтому «вам пример слишком простой» я вопринимаю как комплимент — значит, мне удалось привести понятный пример.
Я имел немного не то. Без злобы, советую глянуть на DDD. Даже для довольно сложных доменов в принципе возможно на стадии проектирования выделить все основные сущности и их методы.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории