• [Перевод] Анемичная модель предметной области — не анти-шаблон, а архитектура по принципам SOLID
    +1
    Необходимо добавить новое поле (или изменить тип данных существующего).
    В конструктор класса Order необходимо передать дополнительный параметр.
    Довольно необычное обоснование что класс перегружен ответственностью…

    SRP ни в коем случае не призывает делать в одном классе ровно один метод.

    Если бы базовые библиотеки языка C# были написаны в том духе, в котором предлагает автор, например вместо
                    var a = DateTime.Now;
                    var b = a.AddDays(1);
                    b = a.AddMinutes(1);
    

    нужно было бы писать
                    var a = new NowService.GetNowTime();
                    var b = new AddDaysService().Calculate(a, 1);
                    b = new AddMinutesService().Calculate(a, 1);
    

    то представьте сколько было бы сервисов, и как сложно было бы пользоваться языком. В продакшн проекте методов как правило тоже не мало, поэтому если будет 100-500 сервисов, то кодом тоже будет сложно пользоваться.
    Другое дело, что при использовании богатой доменной модели тоже нужно с умом распределять методы по классам, а не как попало. Например, кастомер может по-разному валидироваться в разных ситуациях, а добавление в кастомера методов ValidateForA(), ValidateForB(),… ValidateForF() быстро перегрузит кастомера ответственностью; валидность — это не характеристика кастомера, а то, как к нему относится кто-то вовне.
    Ну и не стоит забывать, что даже в классической богатой доменной модели есть место для сервисов (судя по словам Эрика Эванса).

    С автором статьи не согласен. За перевод спасибо.
  • Классы, множества, группы, системы
    +1
    А также на то, что речь идёт про программирование и ООП намекает то, что используется термин предметная область (часть реального мира, рассматриваемая в пределах данного контекста) и указан хаб «Анализ и проектирование систем». Предметная область — это описание мира глазами пользователя. С точки зрения ветеринара, корова — это биологическая система с её историей, а с точки зрения мясника — кусок мяса определенного качества и веса. Соответственно, похудевшая корова будет эквивалентна самой себе в предметной области ветеринара, и не будет эквивалентна самой себе в предметной области мясника (извиняюсь за жестокий пример). Анализ и проектирование систем — этап разработки программного обеспечения, начинающийся с понимания предметной области пользователя. Наиболее распространенный способ описания предметной области в рамках программы — ООП. Чтобы быть уверенным (а большинству — просто чтобы допустить, ведь это программисткий сайт), что статья не имеет никакого отношения к ООП, нужно быть телепатом. Я бы на вашем месте добавил пометку о том, что речь не идёт о ООП, во избежание недопонимания.
  • Entity vs Value Object: полный список отличий
    0
    deleted
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    считайте сколько тестов вам нужно для тестирования конкретного функционала.
    Другими словами можно сказать — сколько тестов нужно для тестирования контракта метода (выполнения постусловий при соблюдении предусловий).
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Тестирование только особенностей тестируемого метода, без привязки к зависимостям?
    Я предлагал использовать вместо зависимости моки внешних сервисов и хранилищ данных, и реальные классы во всех остальных случаях. В случае с Джоном и Петей мы имеем дело с пользовательской историей (т.е. требованием, написанным на языке пользователя) «Замужняя дама должна отклонять просьбу зайти в дом в ночное время от мужчины, не являющегося её мужем». Эта пользовательская история закрепляется тестом. В тесте или при помощи билдера создаётся замужняя дама, или просто в свойство «Муж» передаётся Петя, и потом идёт сама проверка. Допустим, логика проверки на мужа была завязана на isEqual с Петей. В какой-то момент было решено, что в программе представляет интерес только, допустим, надёжность или репутация человека, и два человека с одинаковыми уровнями надёжности можно взаимозаменить в любом месте программы, и isEqual переписали. А про логику замужних дам забыли. Если использовать в качестве isEqual реальную реализацию (или функционал сравнения), то тест может отловить ошибку. А если в тесте использовать не Петю и Джона, а Петю и Васю, то по каким-то причинам может и не отловить. Это тоже ещё одна проблема, что тесты не гарантируют 100% надёжности, поскольку тестируются какие-то отдельные случаи, но не все возможные.

    Если повсеместно использовать DI то этой проблемы не будет и не будет разницы какие тесты вы пишете, интеграционные или модульные.
    Я примерно о том же самом в статье писал.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Любое изменение в коде означает изменение десятка, а то и сотни тестов.
    Ответ на этот вопрос есть в статье. Я приводил ссылку на test builder. Нужно будет изменить builder, а не сотни тестов.

    Соответственно для каждой ветки развития в функции А есть по 3 ветки развития из функции Б.
    3 * 3
    Этот вопрос разбирался в статье: «Согласно TDD, тесты предназначены для проверки функционала (feature), а не путей исполнения программы.». Не считайте пути исполнения программы (или ветки развития, как вы их называете), считайте сколько тестов вам нужно для тестирования конкретного функционала.

    к тестированию не имеют отношения, к тестированию
    Изначально было так:
    Я (в статье): Высказывание 2. Интеграционные тесты в меньшей степени помогают в проектировании, нежели модульные…
    Вы: Это вообще не относится к проектированию и тестированию. Просто везде используется DI и все.
    Я: Если DI и IOC не имеют никакого отношения к проектированию, то я — испанский лётчик.
    Вы: к тестированию не имеют отношения, к тестированию

    Что-то я ничего не понял. По-вашему, тесты к тестированию не имеют отношения? Что вы вообще хотите спросить\сказать?
    Если что, речь шла про тесты в рамках TDD.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    А вам самому не кажется странным, что тест то модульный, то сразу интеграционный и это постоянно меняется, причём при изменении не теста, а реализации?
    Нет, не кажется. Если тип теста (интеграционный \ модульный) зависит от реализации того, что он тестирует (по определению теста) и от реализации самого теста (ухода от реализации «лишних» классов в тесте за счёт мокирования), то вполне логично, что при изменении одной из реализаций или сразу двух тип теста может измениться.

    И я уже задавал вопрос — почему при использовании стандартных классов он остаётся модульным, а при нестандартных перестаёт таковым быть?
    На этот вопрос я уже отвечал. Если результат теста зависит от реализации нескольких модулей исходного кода программы, то тест интеграционный (по определению). Исходный код — текст программы на языке программирования. Язык программирования и исходный код программы — это не одно и тоже. Стандартные классы (int,bool,string,DateTime и пр.) — это часть языка, а не модули исходного кода программы.

    Вообще, по поводу некоторых терминов можно долго и безрезультатно спорить. Особенно в молодых или неточных науках, где терминология не до конца устоялась и со временем претерпевает изменения, и один и тот же термин может означать немного разные явления в разных кругах. Не исключено, что до появления ООП под интеграционным тестированием понималось нечто другое. Также не исключено, что некоторые люди до сих пор понимают под интеграционным тестированием тоже самое, что понимали до появления ООП. Что под интеграционным тестированием понимаю лично я и те программисты, с которыми я общался, я вам рассказал. И, судя по этой статье, некоторые заокеанские коллеги понимают под интеграционным тестированием тоже самое. Можно конечно в начало статьи поместить длиннющий список определений, что подразумевается под мокой, мокированием, тестом, функционалом, рабочим процессом и прочим. Но что под этим подразумевается и так ясно из контекста. Статьи по программированию — это, как правило, не строгое научное доказательство программистких теорем в виде кванторов, а передача опыта и идей. Я бы не хотел читать статьи, в которых половина текста посвящена определениям исходного кода, рабочего процесса и прочего.

    В статье было сказано, что понимается под интеграционным тестом (I use the term integrated test to mean any test whose result (pass or fail) depends on the correctness of the implementation of more than one piece of non-trivial behavior.) и шли рассуждения, почему интеграционных тестов не надо бояться. По сути статьи вопросы есть?
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Есть формальные определения. Все подумали и согласись, что вот это будем называть так-то. Если вам определения не нравятся, то это, скорее всего, ваша проблема, а не тех миллионов или миллиардов людей, которые этим определением успешно пользуются.

    [Ирония on]Недавно прочитал на микроволновке «не помещать внутрь домашних животных», а у меня на балконе карликовый жираф живёт. Балкон — хоть и часть дома, но такая незначительная, прям вообще не часть дома. Я там кроме рассады и жирафа ничего не держу. Какая же это часть дома?! Это часть природы! Да и жираф — милый такой, совсем не животное. Помыл я жирафа, и решил посушить его в микроволновке, а она возьми и сломайся! Но в гарантийном ремонте мне отказали. Не хотят соглашаться, что жираф с балкона — не домашнее животное. Ну просто глупцы какие-то! [Ирония off]

    Надеюсь вас история повеселила.
    P.S. История придуманная. Зоозащитники, узбагойтесь.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Например, если меняется внутренняя реализация метода isEqual возвращающая в некоторых случаях false там где возвращала true, то у нас поломается только 1 юнит-тест и нужно будет дополнить его в связи с изменениями. В случае же интеграционного тестирования у нас может половина тестов покраснеть, хотя реально сделанные изменения ничего не ломают и ни как не влияют на корректность результата.

    Есть у вас в программе Петя и John, и функция isEqual, которая их различает. И вот в один прекрасный день вы изменяете isEqual и тест на неё, и теперь она их не различает. А ещё есть тест на то, что жена Пети не пускает в три часа ночи незнакомых пьяных мужчин. И вот после изменения isEqual этот тест краснеет. Вы ругаете интеграционное тестирование, и удаляете этот тест, ведь «сделанные изменения ничего не ломают и ни как не влияют на корректность результата». Примерно через год после этого приходит Петя и слёзно спрашивает вас, почему его жена пускает по ночам пьяных незнакомых мужчин, и почему в его семье родился негр, хотя ни у него в роду, ни у его жены негров отродясь не было.

    Это вообще не относится к проектированию и тестированию. Просто везде используется DI и все.
    Если DI и IOC не имеют никакого отношения к проектированию, то я — испанский лётчик.

    Ведь мы же используем реальные зависимости, а не моки, соответственно мы должны протестировать не только функциональность тестируемого метода, но и функциональность используемых зависимостей (в этом же весь смысл).
    Прочитайте определение интеграционного теста… Там не сказано, что целью интеграционного теста является тестирование нескольких модулей. Там сказано, что результат прохождения (зелёный или красный) зависит от корректности реализации нескольких модулей (или что модули тестируются в группе). Это большая разница. Определение диктует то, как интеграционный тест должен быть реализован, но не то, с какой целью вы будете его реализовывать, и как вы будете его использовать. Цель определяет TDD. Интеграционные и модульные тесты в плане цели в TDD не различаются — это тестирование одной функциональности. Допустим есть функционал А, Б, В. Каждый функционал реализован в отдельном классе. Код, реализующий функционал А, использует функционал Б. Код, реализующий функционал Б, использует функционал В. Соответственно вы можете написать 6 модульных тестов на А и Б, по 3 на штуку, и точно также можете вместо них написать 6 интеграционных тестов на А и Б. Если код, реализующий функционал В, не использует никакой другой функционал и никакие другие классы, то на функционал В можно написать только модульные тесты. И никто вас не поколотит, если вы после замены моков на реальные классы не напишите ещё 100-500 тестов. Если Б сломается, то интеграционный тест на А упадёт, а модульный — нет (вспоминаем историю с Петей и негром).
  • Entity vs Value Object: полный список отличий
    +4
    Совершенно верно. В одних программах в рамках DDD адрес будет объектом-сущностью (когда нас интересует уникальность жилища; например, когда у каждого жилища есть какой-то рейтинг или история жильцов), а в других — объектом-значением.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    из Ваших определений следует, что поделив тестируемый метод пополам, без изменения функционала, мы получим интеграционный тест
    Если переместить часть кода тестируемого метода в другие классы, в тестируемом методе вызвать методы этих других классов, и не навесить моки на эти классы, то модульный тест станет интеграционным. Это следует из определений модульного и интеграционного теста на википедии.
    Если вспомнить, что определение Фаулера не содекржит упоминаний, что использование «стандартных» классов нещитово
    Зато это следует из определений модульного теста и исходного кода программы на википедии. (исходный код != (не равно) язык программирования. исходный код — то, что написано на языке программирования)

    Извините, мне не интересно с вами общаться. До свидания.
  • Entity vs Value Object: полный список отличий
    +4
    В классической книге по DDD Эрика Эванса «Предметно-ориентированное программирование» приводится тот же самый пример про человека и адрес, а потом идёт раздел про оптимизацию, и там говорится, что объекты-значения легко подвергнуть денормализации и вставить внутрь таблицы объекта-сущности. Ни слова про то, что поступать так нужно всегда, там нет. Такой совет противоречит DDD. Имхо, вольный пересказ книги у автора статьи не удался.
  • Entity vs Value Object: полный список отличий
    +2
    Имхо, проблема не в примере, а в правиле, которое этот пример иллюстрирует:
    Объекты-значения не должны иметь собственной таблицы в БД.
  • Entity vs Value Object: полный список отличий
    +7
    В вопросе объектов-значений и сущностей важное значение имеет следующее правило: всегда предпочитайте объекты-значения сущностям.
    Это противоречит самому духу DDD. DDD призывает строить дизайн приложения исходя из предметной области. Т.е. выбор Entity vs Value Object делается исходя из уникальности\неуникальности.
    У вас прям сборник вредных советов какой-то получился.
  • Entity vs Value Object: полный список отличий
    +3
    В DDD Aggregation Root для этого есть. Но, автор, видимо, о нём не слышал…
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Вы в TDD можете хоть кодревью добавить, хоть необходимость завтракать. Только это будет уже не TDD.
    А я разве говорил, что получившийся рабочий процесс обязан называться именно TDD? Не приписывайте мне своих фантазий.

    Вывод неправильный.

    Ладно, побуду занудой. Докажем, что интеграционные тесты используются в классическом TDD. Раз 1)классическое TDD, согласно Фаулеру, призывает использовать в тестах реальные объекты там, где это возможно и 2)в программировании возможна ситуация, что в методе одного класса используется метод другого класса, и нет веских обстоятельств, мешающих использовать реальный объект (пункт 2 доказывается примером, который я не считаю нужным приводить), то из этих двух пунктов следует, что в классическом TDD возможна ситуация тестирования одного модуля, обращающегося к другому. А по определению интеграционного теста, это интеграционный тест. Следовательно, интеграционные тесты используются в классическом TDD. Теперь согласны? Или снова будете говорит что всё неправильно, не поясняя где вы заподозрили противоречие?
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Выходит, по-вашему, интеграционные тесты в рамках TDD использовать нельзя? Ну давайте тогда ваше обоснование, почему нельзя.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    А если взять определения из википедии?
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Это косяк дизайна. TDD эту проблему решать не задуман.
    Я об этом в статье и пишу, и предлагаю или TDD-шный цикл разработки изменить, или не мочить всё подряд без проверки задекларированного в моках поведения.

    Если все женщины люди
    Не если, а безусловно люди. Раз вы о прекрасном поле заговорили, то, я так понимаю, тема применения интеграционных и модульных тестов в классическом TDD исчерпана?
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Вообще, не вижу смысла париться над разделением интеграционные тесты — модульные. Актуальнее проблема классическое TDD — мокисткое.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Хотя, если есть возможность пропатчить стандартную библиотеку применительно к только какому-то одному модулю (классу), и есть возможность сделать это внутри самого модуля, то тестируется содержимое одного модуля, и тест модульный.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Если тестировать методом черного ящика и писать тест до реализации, то непонятно, получится он интеграционным или модульным — зависит от того, обращается ли к методам других классов ReverseString. Тип теста можно будет узнать только после реализации ReverseString. Кот Шрёдингера какой-то получается.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Тесты тестируют модули исходного кода. Когда вы патчите стандартную библиотеку, появляется исходный код. Раз несколько модулей исходного кода — значит тест интеграционный.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Ну вы же сами себе отвечаете: не надо править мок, нет такого этапа.
    Так в том то и дело, что этапа нету, а править надо, раз поведение мокированного класса изменилось.

    И как это обосновывает применимость его в философии TDD?
    По Фаулеру, The classical TDD style is to use real objects if possible and a double if it's awkward to use the real thing. Поскольку real objects, то тест интеграционный.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    А если я вместо стандартной библиотеки времени использую нестандартную, то уже не модульное?
    Совершенно верно.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Если у вас код не зависит от поведения класса мока, то вам не надо править мок.
    Речь шла не о том, в каких случаях нужно править мок, а о том, как решить, что вот прямо сейчас пришло время поправить мок. В рамках TDD все этапы чётко расписаны, что и после чего нужно делать. И правка моков не вписывается ни в один этап.

    И вы легко приведёте пруфы?
    Пруфы содержаться в статье. Ссылка на определение интеграционного теста и ссылка на определение классического и мокисткого TDD.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Вообще, разница между юнит-тестами и интеграционными, на мой взгляд не так велики. Тест, в котором используются какие-то библиотеки языка, которые решили не мокировать (те же объеты даты и времени) можно считать как юнит-тестом, так и интеграционным.

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

    В статье приведена ссылка на статью из русскоязычной википедии, в которой дано следующее определение: модульное тестирование, или юнит-тестирование (англ. unit testing) — процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы. (Соответственно в статье я подразумеваю под модульным тестированием именно это.) Также приведена ссылка на определение интеграционного теста: одна из фаз тестирования программного обеспечения, при которой отдельные программные модули объединяются и тестируются в группе.

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

    При TDD разработка мне видится так:
    1. при необходимости внесения изменений человек описывает парочку интеграционных тестов, если это возможно, которые показывают как всё должно работать в целом.
    Нет, в рамках TDD тесты пишутся не на «всё в целом», а на конкретный функционал. При этом в классическом TDD игнорируется тот факт, что этот функционал может использовать другой функционал, а в мокистком TDD эта связь с другим функционалом разрывается. Это важное различие между классическим и мокистким TDD. Классическое TDD проверяет корректность функционала, а мокисткое — корректность поведения класса.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Хотя, в чём-то вы правы :) В проекте в любом случае будет содержаться некоторая доля модульных тестов: для тех классов, внутри которых нет обращений к методам других классов. Формально и фактически такие тесты не будут интеграционными. Они будут потенциально интеграционными. Если в них добавится обращение к методам других классов, то по классическому TDD эти тесты будут преобразованы и станут интеграционными фактически. В статье шло скорее не сравнение интеграционных тестов и модульных, а сравнение мокисткого подхода и классического. Раз это не так очевидно, то добавлю в статью, что даже при классическом подходе какая-то часть тестов будет модульными. Хотя, имхо, это и так очевидно, вроде бы.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Мокисткое правило — «Всегда мокируйте любое поведение объектов». Правило классического TDD — «Всегда используйте настоящие объекты, если это возможно». Если вы изобретёте новое правило, то это будет новым подходом. Не представляю, как можно быть где-то посередине: использовать немного настоящий объект, который является немного мокой. Может, можно как-то извратиться и сделать, но не представляю зачем и как.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    при полноценном TDD сначала пишется интеграционный тест, а затем, при необходимости, юнит-тесты на уровень ниже.
    А почему не «а затем, при необходимости, интеграционные тесты на уровень ниже»?

    Подходы только юнит или только интеграционные — это две крайности.
    Довольно спорное философское утверждение. А быть негром или европеоидом — это тоже две крайности? Есть две школы (классическая и мокисты), которые по-разному подходят к TDD. В какой-то фиксированный момент времени вы либо ведёте себя как последователь одной школы, либо как последователь другой. В противном случае вы ведёте себя как человек, не следующий учению ни одной из школ, и вы можете основать свою школу, если сумеете систематизировать свои знания и формально описать новый подход (а не просто сказав, что выбираем тип теста в зависимости от дня недели, не объяснив, почему модульные тесты лучше писать по вторникам, а интеграционные — по пятницам). Возможно эта новая школа будет сочетать лучшие традиции предыдущих двух школ, и они исчезнут, слившись в одну. Но пока что этого не произошло. При всём уважении, желание быть всегда «где-то посередине» — тоже крайность, имхо.
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Во-первых, надо разделять классы так, что бы это не было проблемой.
    А можете пояснить, как именно нужно разделить классы? Не представляю, как перераспределение кода по классам способно решить эту идеологическую проблему, если изменение моков не вписывается ни в один из этапов TDD-шного цикла.

    В случае, если вы тут не будете использовать моки — у вас при изменении поведения упадёт 1000 тестов. Да, заметить это легче, но чинить сложнее.
    Этот вопрос подробно рассматривался в статье.

    Во-вторых, вы зачем-то смешиваете «classical TDD» и интеграционные тесты. Это совершенно разные вещи.
    Тест — это фрагмент кода, TDD — это философия, рабочий процесс. Интеграционные тесты используются в классическом TDD. Я не говорил, что тест = TDD.

    Интергационный тест — это более чем про одну feature, и к TDD имеет очень отдалённое отношение.
    А можете пояснить, почему по-вашему интеграционные тесты не имеют отношения к TDD?
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    0
    Представьте такую ситуацию. Гейм-дизайнеры решили, что какой-то перк (или способность) слишком сильная, и для соблюдения баланса решили уменьшить её эффективность в 2 раза. Или путём уменьшения в 2 раза какой-то характеристики перка, или путём добавления каких-то новых условий, которые необходимы, чтобы перк сработал. При этом этот перк должен остаться у тех же персонажей, которые им владели изначально. В этой ситуации проще в коде класса существующего перка константу изменить или условие добавить, чем удалять отовсюду старый перк и добавлять новый. А раз меняется бизнес-логика работы класса, то и тест нужно изменить. При этом в рамках TDD тест нужно изменить в первую очередь.
    Ситуации разные бывают. Иногда при изменении функционала нужно существующие тесты изменить, иногда их нужно удалить, иногда нужно новые написать, не трогая существующих, — всё от ситуации зависит. Но если вы меняете логику работы класса, то нужно в любом случае взглянуть и на его тесты, и что-то с этими тестами сделать (добавить\удалить\изменить)
  • Проблема дублирования и устаревания знания в mock-объектах или Интеграционные тесты — это хорошо
    +2
    На стадии сопровождения часто требуется изменить действующий функционал. Например, новую колонку добавить в уже существующий отчёт, новый перк добавить уже существующему персонажу, внешнему сервису, с которым уже налажено взаимодействие, какие-то другие передать и так далее. В таких случаях зачастую проще изменить существующий тест и существующий класс, чем стереть тест и класс и написать всё заново. Это не ломание рабочего теста и рабочего класса, а просто приведение их в соответствие с новой бизнес-логикой и новыми требованиями заказчика.
  • Парсинг на языке Prolog
    +1
    Тут немедленно возникает разумный вопрос: под какие же задачи пролог заточен?
    Вот этот комментарий частично отвечает на ваш вопрос. Пример автоматического доказательства теоремы можете посмотреть в этой статье, я тоже на эту тему писал.
  • Парсинг на языке Prolog
    +1
    Ну да, пролог не для GUI. В программе, написанной на языке логического программирования, решение задачи часто ищется посредством хорошо оптимизированного брутфорса. Изначально это задумывалось как перебор математических фактов с целью доказательства теорем. Можно создать обычную прикладную программу на прологе, и она будет гораздо гибче и понятнее, чем программа из любой другой парадигмы, но будет она очень тормознутой — ибо брутфорс. Можно написать на прологе и быструю программу, но тогда нужно будет не просто описывать известные факты о системе, а как бы направлять ход выполнения программы, по сути возвращаясь к императивному программированию. Логическое программирование тем и ценно, что программист, например, описывает что такое ток, проводочки и диоды, задаёт максимальное количество элементов на микросхеме, и просит программу собрать ему синхрофазотрон. Без шуток, этот очень оптимизированный брутфорс творит чудеса, и Ватсон свидетель чудес его!
  • Парсинг на языке Prolog
    0
    предикат lexr/2 последовательно вызывает три предиката: list_text/2, delb/2, lexr1/2.

    Эта фраза не совсем корректна, в логической парадигме нет понятия «вызов функции» или вызова чего бы то ни было. Точно также нет присвоения переменных.
    Согласен, имена предикатов в данном случае малоинформативные.
  • Парсинг на языке Prolog
    0
    Я указываю проблему Prolog это интеграция.

    Не занимался интеграцией с прологом, но первое, что приходит в голову — это передача итоговых данных по TCP на localhost. И это не единственный способ.
  • Парсинг на языке Prolog
    0
    Сделать на нем какой либо интерфейс взаимодействия с пользователем тот еще цирк.

    Есть Visual Prolog. Например, button::defaultAction :- Name = idc_ancestordialog_personname:getText(). В логической парадигме программирования нет присвоения переменных и вызовов функций, поэтому концептуально это можно перевести как «defaultAction истинно, когда Name совпадает с тем, что ввёл пользователь в окошко». Потом в каком-то месте программы утверждается, что defaultAction истинно (напомню, вызовов функций нет), и Prolog начинает искать все Name, которые совпадают с тем, что ввёл пользователь. Для этого у пользователя просят ввести данные. После этого считается, что Name определён. В общем-то да, GUI в логической парадигме немного запутаннее, чем в функциональной или объектно-ориентированной, но тем не менее, вполне осуществим.
  • Disposable без границ
    0
    Имхо, автор открыл (ну, или поспособствовал открытию) замечательный способ множественного наследования из мира ненормального программирования. Вместо того, чтобы класс реализовывал IPrintable, IExcelable, IDisposable можно написать три универсальных класса Printable, Excelable, Disposable, способных печатать и диспозить что угодно. Всю логику передаём в универсальные классы через делегаты. Поскольку в C#/Java нет множественного наследования, то делаем Printable[Excelable[Disposable[ViewModel]]].
    Можно вообще сделать просто один класс Doer[T], делающий вообще всё что угодно. Пусть у класса 3 метода, тогда переменная будет иметь тип Doer[Doer[Doer[StreamReader]]](). Код вызова 1го метода: doer.Do(). Второго: doer.Value.Do(). Третьего: doer.Value.Value.Do(). Логику методов можно передавать через делегаты: new StreamReader.ToDoer(x => x.Dispose()).ToDoer(x => x.Value.ToString()).ToDoer(x => x.Value.Value.GetHashCode()). Чудесненько.

    Имхо, в данном случае автор предлагает своеобразный способ, как «переопределить» Dispose в StreamReader, не реализуя наследника от StreamReader. Это незначительно экономит время: вместо объявления класса и метода нужно просто передать тело метода через делегат в ToDisposable(Action[T] delegate). Цена: использование нестандартного подхода, замена переменных типа StreamReader на Disposable[StreamReader], возможность переопределить только один метод (иначе приходим к Printable[Excelable[Disposable[StreamReader]]], а это уже явный перебор). Этот способ имеет смысл, когда нужно переопределить метод в запечатанном классе без публичных конструкторов. Однако автор предлагает использовать его повсеместно при любом освобождении ресурсов. Непонятно, почему бы просто не реализовать наследника StreamReader, раз уж так сильно хочется переопределить Dispose. Хотя не до конца понятно, а зачем его переопределять. Взяли из пула соединение, передали в функцию, функция отработала, вернули соединение в пул.
  • Disposable без границ
    +1
    Допустим, у вас в ViewModel «на одну ответственность меньше», а сколько тогда ответственностей у Disposable[ViewModel]? А если понадобится добавить функцию печати и импорта в Excel, вы напишите Printable<Excelable<Disposable[ViewModel]>>?