Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>Это никакой не мок ! Мок симулирует поведение.
EP>Здесь тоже симуляция поведения.
Точнее отсутствие такой симуляции.
I>>Приведеный тест есть обычный, даже тривиальный поведенческий. Retry — плохой пример.
EP>Retry как раз пример того где для тестирования контракта нужен mock.
Я уже понял, у тебя любая лямбда сразу станет моком.
Здравствуйте, Ikemefula, Вы писали:
I>>>Это никакой не мок ! Мок симулирует поведение. EP>>Здесь тоже симуляция поведения. I>Точнее отсутствие такой симуляции.
Mock в самом чистом виде, с проверкой порядка вызовов (в данном случае количества)
I>>>Приведеный тест есть обычный, даже тривиальный поведенческий. Retry — плохой пример. EP>>Retry как раз пример того где для тестирования контракта нужен mock. I>Я уже понял, у тебя любая лямбда сразу станет моком.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>>>Приведеный тест есть обычный, даже тривиальный поведенческий. Retry — плохой пример. EP>>>Retry как раз пример того где для тестирования контракта нужен mock. I>>Я уже понял, у тебя любая лямбда сразу станет моком.
EP>Как? Ну как ты сделал это вывод?
Очень просто — из любой лямбды можно кидать исключение, следовательно, любая лямбда автоматически будет симуляцией поведения, поскольку исключение можно и бросать, а можно и не бросать.
Здравствуйте, Ikemefula, Вы писали:
I>Очень просто — из любой лямбды можно кидать исключение,
Да.
I>следовательно, любая лямбда автоматически будет симуляцией поведения,
Здесь логическая ошибка: из того что какой-то инструмент можно использовать для симуляции поведения, не следует что этот инструмент используется только при симуляции поведения
Здравствуйте, IT, Вы писали:
IT>Но есть ещё один тип кода — тот, который управляет кодом, который делает работу. При этом если код, который делает работу, делать максимально независимым и отчуждаемым, а управляющий код делать максимально примитивным, то появляется реальная возможность без проблем жонглировать кодом как вздумается. Передача юниту поведения убивает эту возможность наповал. В общем, coupling and cohesion в полный рост.
Не совсем ясно, что ты хочешь сказать
Любой код, гипотетически, можно сделать предельно простым.
Вопрос — всегда ли будут сроки, бюджет на такую работу ?
Поведение и взаимодействие как правило практически всегда самые сложные вещи. Жонглируй как хочешь ,но юзер собтсвенно деньги платит именно за поведение-взаимодействие.
Второй вопрос — в какой части кода будет описываться это поведение-взаимодейтсвие и как эту часть кода правильно тестировать ?
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>следовательно, любая лямбда автоматически будет симуляцией поведения,
EP>Здесь логическая ошибка: из того что какой-то инструмент можно использовать для симуляции поведения, не следует что этот инструмент используется только при симуляции поведения
Здесь нет ошибки. Любой компонент, который использует ioc на лямдах, будет демонстрировать моки в полный рост.
Здравствуйте, Ikemefula, Вы писали:
I>>>следовательно, любая лямбда автоматически будет симуляцией поведения, EP>>Здесь логическая ошибка: из того что какой-то инструмент можно использовать для симуляции поведения, не следует что этот инструмент используется только при симуляции поведения I>Здесь нет ошибки.
Здесь логическая ошибка — ты пытаешься из утверждения "исключения в лямбдах можно использовать для симуляции поведения реальных объектов", сделать утверждение "исключения в лямбдах используются ТОЛЬКО для симуляции поведения реальных объектов" и из этого пытаешься делать какие-то выводы
I>Любой компонент, который использует ioc на лямдах, будет демонстрировать моки в полный рост.
template< class InputIt, class OutputIt, class UnaryOperation >
OutputIt transform( InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op );
В качестве unary_op можно передать:
1. Операцию которая делает x + x — это вполне реальная операция, а не симуляция поведения реальной операции.
2. Нечто что будет запоминать (а потом и проверять) порядок вызовов и их аргументы, а вместо сложения будет возвращать данные из заранее подготовленного массива, по порядку, симулируя поведение реальной операции. Вот это mock.
P.S. Тут вообще говоря не важна форма — замыкание или явный объект — важно же что и как проверяется.
От того что вместо явного класса с полями используются локальные переменные с замыканием — смысл не меняется.
Здравствуйте, Ikemefula, Вы писали:
I>Вопрос — всегда ли будут сроки, бюджет на такую работу ?
На тесты бюджет есть, а на дизайн приложения нету?
I>Поведение и взаимодействие как правило практически всегда самые сложные вещи. Жонглируй как хочешь ,но юзер собтсвенно деньги платит именно за поведение-взаимодействие.
У тебя продвинутые юзеры, если они так тонко разбираются в дизайне приложения и решают за какую именно его часть тебе платить.
I>Второй вопрос — в какой части кода будет описываться это поведение-взаимодейтсвие и как эту часть кода правильно тестировать ?
Я не даром упирал на примитивизм. Чаще всего код теста сам по себе является таким управляющим кодом. Но можно его тестировать в интеграционных тестах, если очень хочется. А можно вообще не тестировать. Ошибка в таком коде обычно приводит к невозможности выполнения всего или части приложения. Т.е. либо всё приложение никуда не взлетит, либо упадут все тесты, завязанные на такой код. В общем, с тестированием такого кода обычно проблем нет. Иначе это уже полноценный алгоритм. Да и сама идея жонглирования предполагает, что управляющий одними и теми же алгоритмами код может постоянно меняться и присутствовать в приложении в нескольких вариациях.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Последнее следует пояснить. Лично я для себя выделяю несколько, принципиально отличающихся друг от друга, типов кода. Например, всевозможные структуры данных, DTO, AST, ADT и прочие контейнеры — это один тип кода, задача которого заключается в представлении данных. Тестирование такого кода в языках со строгой типизацией имеет исчезающе мало смысла и является признаком весьма бурно прогрессирующей паранойки.
Тестирование структур данных уровня деревьев поиска, хэш-таблиц и т.п. это по-твоему паранойя?
IT>Так вот разделение кода на типы и примитивизация управляющего кода — это самый простой способ. Независимые, самодостаточные, легко отчуждаемые юниты не представляют угрозу увеличения сложности приложения, а вот управляющий код является чуть ли не фундаментальной основой большинства подобных проблем.
Стараюсь при первой же возможности выдирать обобщённый/независимый код в библиотеку — помимо всего прочего, как раз потому что его намного проще тестировать чем в случае когда он переплетён с кодом уровня приложения.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Тестирование структур данных уровня деревьев поиска, хэш-таблиц и т.п. это по-твоему паранойя?
Структура данных — это описалово. Чего в ней тестировать? Тестировать нужно алгоритмы, которые с этой структурой работают, а не её саму. Её саму тестирует компилятор.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
EP>>Тестирование структур данных уровня деревьев поиска, хэш-таблиц и т.п. это по-твоему паранойя? IT>Структура данных — это описалово.
Это не только описалово, но ещё и алгоритмы поддерживающие инварианты этой структуры (повороты для деревьев поиска, pop/push для куч и т.п.)
IT>Чего в ней тестировать?
Например то что поиск в хэш-таблице возвращает именно те элементы, которые требовалось. То что при росте у нас не пропадают элементы. То что эти операции вообще останавливаются.
IT>Тестировать нужно алгоритмы, которые с этой структурой работают, а не её саму.
Внешние? Или используемые внутри? (например std::make_heap используется внутри std::priority_queue)
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Например то что поиск в хэш-таблице возвращает именно те элементы, которые требовалось. То что при росте у нас не пропадают элементы. То что эти операции вообще останавливаются.
Здравствуйте, Ikemefula, Вы писали:
EP>>Например то что поиск в хэш-таблице возвращает именно те элементы, которые требовалось. То что при росте у нас не пропадают элементы. То что эти операции вообще останавливаются. I>И для чего тебе моки в этом случае ?
Здравствуйте, IT, Вы писали:
IT>На тесты бюджет есть, а на дизайн приложения нету?
Его необязательно хватит на всё работу в должном качестве. В аутсорсе часто код пишется вот так — вот вам 100 долларов, сделайте за пол-часа сколько сможете и с этим идём в релиз. И я видел очень много случаев, когда основные деньги проекта зарабатывались именно вот таким вот способом. Скажем, бывало так — накидали на коленке сотню, от силы две строчек кода, подсунули кастомеру и выдал на руки чуть не налом шестизначное число денег.
С одной стороны, понятно, что тупик. С другой стороные есть умельцы, у которых таких "тупиков" и желающих расстаться с деньгами не то что не переводится, а наоборот, растет от года к году.
I>>Поведение и взаимодействие как правило практически всегда самые сложные вещи. Жонглируй как хочешь, но юзер собтсвенно деньги платит именно за поведение-взаимодействие.
IT>У тебя продвинутые юзеры, если они так тонко разбираются в дизайне приложения и решают за какую именно его часть тебе платить.
Назови хотя бы один пример софта, нужного скажем не-ИТ специалисту, который бы не обладал поведением и за него платились бы внятные деньги.
IT>Я не даром упирал на примитивизм. Чаще всего код теста сам по себе является таким управляющим кодом. Но можно его тестировать в интеграционных тестах, если очень хочется.
Я вот не сильно понимаю грань между интеграционными и блочными тестами. Вот у меня есть модель документа, фактически наиболее похожа на HTML DOM или XML DOM, с похожими свойствами. Один узел можно потестировать — это простой блочный тест. Вот уже два узла, внезапно, создают проблемы, потому что от поведения деваться некуда. DOM он целиком и полностью про поведение и состояние. Чуть что сложнее теста одного единственного узла, то на ровном месте вылазят моки
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здесь логическая ошибка — ты пытаешься из утверждения "исключения в лямбдах можно использовать для симуляции поведения реальных объектов", сделать утверждение "исключения в лямбдах используются ТОЛЬКО для симуляции поведения реальных объектов" и из этого пытаешься делать какие-то выводы
Хочешь ты или не хочешь, но лямбду всегда можно рассматривать как симуляцию поведения. Помещаешь её в тестовый код, опаньки, она становится моком.
I>>Любой компонент, который использует ioc на лямдах, будет демонстрировать моки в полный рост.
EP>Опять логическая ошибка.
I>>Вот, скажем, items.OrderBy(item => item.Name) — Опаньки, Мок !
EP>Конкретный пример
Не интересно. Я показал простой пример, что с ним не так ?
Здравствуйте, Ikemefula, Вы писали:
EP>>Здесь логическая ошибка — ты пытаешься из утверждения "исключения в лямбдах можно использовать для симуляции поведения реальных объектов", сделать утверждение "исключения в лямбдах используются ТОЛЬКО для симуляции поведения реальных объектов" и из этого пытаешься делать какие-то выводы I>Хочешь ты или не хочешь, но лямбду всегда можно рассматривать как симуляцию поведения.
Нет, нельзя
си-му-ля́-ци·я
Значение
действие по значению гл. симулировать; создание ложного впечатления, представления о чем-либо; притворство
Синонимы
симулирование, притворство
Если лямбда действительно что-то делает, а не "притворяется" — то это не симуляция.
I>>>Вот, скажем, items.OrderBy(item => item.Name) — Опаньки, Мок ! EP>>Конкретный пример I>Не интересно. Я показал простой пример, что с ним не так ?
С ним всё так — это не мок, здесь нет симуляции поведения реального объекта (а уж тем более нет характерной для моков проверки последовательности действий), здесь просто реальный объект со своим поведением.
А вот в случае когда у нас есть реальный connect, который может сфейлится, а может и нет, и нам нужно протестировать например что после пяти попыток retry(con, 5) прекращает попытки, либо что при удачном соединении на пятой попытке ничего не ломается — и мы для этого заводим объект (или лямбду — не суть важно) который симулирует поведение реального connect в плане фейлов, да ещё к тому же проверяет последовательность вызовов, и помимо этого не имеет никакого даже близкого к разумному применению в реальном коде — то это самый что ни на есть Mock.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Если лямбда действительно что-то делает, а не "притворяется" — то это не симуляция.
То есть, пример Retry, лямбда которая действительно бросает исключение,а не "притворяется", не симуляция, следовательно и не мок. Так что ли ?
I>>>>Вот, скажем, items.OrderBy(item => item.Name) — Опаньки, Мок ! EP>>>Конкретный пример I>>Не интересно. Я показал простой пример, что с ним не так ?
EP>С ним всё так — это не мок, здесь нет симуляции поведения реального объекта (а уж тем более нет характерной для моков проверки последовательности действий), здесь просто реальный объект со своим поведением.
А как выяснить, где просто, а где не просто ?
EP>А вот в случае когда у нас есть реальный connect, который может сфейлится, а может и нет, и нам нужно протестировать например что после пяти попыток retry(con, 5) прекращает попытки, либо что при удачном соединении на пятой попытке ничего не ломается — и мы для этого заводим объект (или лямбду — не суть важно) который симулирует поведение реального connect в плане фейлов, да ещё к тому же проверяет последовательность вызовов, и помимо этого не имеет никакого даже близкого к разумному применению в реальном коде — то это самый что ни на есть Mock.
В книгах, моки это когда
объекты зависят от внешней системы (у тебя нет базы данных)
имеют недетерминированое или изменяемое поведение (в твоем случае детерминированое и неизменное)
трудно изловить в рантайме (снова мимо)
медленные (снова не то)
информацию можно получить только косвенно (у тебя именно так, как требует апи)
Ты хочешь привязать моки вообще к тестированию любого поведения. Эдак окажется, что Retry(5) — здесь вот 5 это mock-data ! А любая лямбда у этого метода будет мок-объект.
Вот и получается, что у тебя всё есть моки. На самом деле моки решают одну простую задачу — они изолируют компонент или связку от определенных аспектов — от внешней системы, от сложного поведения и тд и тд. Изолируют и позволяют сделать ассерт. Это принципиальное отличие, самого ассерта унутре может и не быть, но мок позволяет его сделать. Самое главное — изоляция.
Теперь покажи, что в твоем случае от чего изолируется.