Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.145.173.112] |
|
Страницы: (6) « Первая ... 2 3 [4] 5 6 все ( Перейти к последнему сообщению ) |
Сообщ.
#46
,
|
|
|
ФП достаточно декларативно. Да даже в рамках традиционного ИП/ООП можно писать достаточно декларативно. |
Сообщ.
#47
,
|
|
|
Цитата korvin @ – откуда мы знаем, что submit вообще вызовет accounts.load? – откуда мы знаем, что submit вызовет accounts.load с параметром sourceAccount.id? – откуда мы знаем, что submit не вызовет других методов accounts? Не знаю как в Java, а NSubstitute умеет проверять какие методы, в каком порядке, сколько раз и с какими аргументами были вызваны. Вообще не вопрос. Цитата korvin @ Т.е. наличие параметра Accounts в публичном конструкторе — это часть контракта, публичная внешняя зависимость, а как именно используется экземпляр Accounts — это деталь реализации и «знание» этой детали в тесте — прямое нарушение инкапсуляции. Никто не мешает тебе изменить контракт на public void submit(Accounts accounts, Request r) { final var source = accounts.load(r.source); final var destination = accounts.load(r.destination); validate(source, destination, r.amount); final var transaction = Transaction.fresh(r.source, r.destination, r.amount); transactions.store(transaction); notifyUsers(source, destination, transaction.id); } Просто разработчику было лень пихать accounts в каждый вызов Да брось! Теже яйца, только в профиль Теперь тебе надо мокать Request и точно также мокать 2 аккаунта |
Сообщ.
#48
,
|
|
|
Интеграционный тест: правильно ли твой калькулятор пользуется логгером. =) |
Сообщ.
#49
,
|
|
|
Цитата korvin @ Интеграционный тест: правильно ли твой калькулятор пользуется логгером. =) Это если речь идет о интеграционном тесте, он же пишет юнит тест. |
Сообщ.
#50
,
|
|
|
Цитата korvin @ Да ни фига, просто маскируют императивность монадами да хвостовыми рекурсиями. И в конечном счете все те же рефакторинги, тесты и косяки. Так уж сложилось, что наш физический мир императивен, поэтому копни любую декларативность и под ней будет архитолстенный слой махровой императивщины. ФП достаточно декларативно. |
Сообщ.
#51
,
|
|
|
В данном случае это просто пример В реальном мире это может быть не логгер, а, скажем, нужно совершить 3 попытки коннекта и потом кинуть исключение. Или убедиться, что какая-то функция была вызвана с опреденными параметрами. PS: у нас есть список строк по которому идет поиск ошибок в логах. Если изменяется выводимая в лог строка, то надо и этот список проапдейтить. Проверка контента в данном случае работает на ура. Правда должен признать, что это скорее прихоть шефа Он хочет когда-нибудь достичь дзена и вручить этот список клиентам, чтобы они сами научились локализовывать проблемы. |
Сообщ.
#52
,
|
|
|
Цитата Fester @ Не знаю как в Java, а NSubstitute умеет проверять какие методы, в каком порядке, сколько раз и с какими аргументами были вызваны. Вообще не вопрос. Да вопрос не в этом, как «технически» узнать. Mockito тоже всё это умеет. Вопрос в том, какого чёрта мы это как бы знаем, это деталь реализации, которую юнит-тест знать не должен. Цитата Fester @ Никто не мешает тебе изменить контракт на Это никак не решает проблему: в интерфейсе Accounts десяток методов, метод submit использует только один из них. А может два, а может только при каких-то условиях. Об этом ничего не сказано в сигнатуре метода (входных параметрах), поэтому и юнит-тест этих деталей знать не должен. А значит, мокать нужно не отдельные вызовы метода Accounts.load, а все методы Accounts, т.е. фактически делать не мок, а стаб. Либо в качестве зависимости нужно не Accounts указывать, а интерфейс, определённый в модуле Transfer и имеющий точно нужную сигнатуру, а ля interface AccountLoader { Account load(Account.ID id); } и его использовать в сигнатуре метода. Цитата Fester @ Теперь тебе надо мокать Request и точно также мокать 2 аккаунта Не надо мне мокать Request, я же написал пример теста, где там мок? Добавлено Цитата Wound @ Это если речь идет о интеграционном тесте, он же пишет юнит тест. А это ещё одна проблема с моками: их любители часто думают, что пишут юнит-тест, но фактически получают интеграционный =) Добавлено Цитата applegame @ Да ни фига, просто маскируют императивность монадами да хвостовыми рекурсиями. Никто ничего не маскирует, все пишут декларативную функциональную композицию. =) Цитата applegame @ Так уж сложилось, что наш физический мир императивен Нет, наши физические компьютеры имеют императивную модель, а мир чисто конкурентно-событийный. Некоторые императивщики, конечно, пытаются переписать историю, но физически она не меняется. |
Сообщ.
#53
,
|
|
|
Цитата Fester @ В данном случае это просто пример В реальном мире это может быть не логгер, а, скажем, нужно совершить 3 попытки коннекта и потом кинуть исключение. Или убедиться, что какая-то функция была вызвана с опреденными параметрами. Зачем три попытки коннекта тестировать юнит тестами? Добавлено Цитата korvin @ А это ещё одна проблема с моками: их любители часто думают, что пишут юнит-тест, но фактически получают интеграционный =) Ну вот как в примере выше Fester и пишет UT с моками, тестирует коннекты и логеры и называет это UT. |
Сообщ.
#54
,
|
|
|
Цитата korvin @ Вопрос в том, какого чёрта мы это как бы знаем, это деталь реализации, которую юнит-тест знать не должен. Так и во втором варианте надо знать детали Ну или можно не знать детали и мокать все, но тогда это будет, как ты сказал стаб Цитата korvin @ Не надо мне мокать Request, я же написал пример теста, где там мок? Я не силен в Java, так что заранее прошу прощения, но: public Response submit(Request r) { validate(r); return transaction(r); } private void validate(Request r) { checkCompliance(r); checkBalance(r); } private void checkCompliance(Request r) { // исходя из твоей логики, ты ничего не должен знать о реализации, а значит тебе придется мокать все проперти и методы как у source, так и у destination. if (r.source.val().hasSameOwnerAs(r.destination.val())) { return; } // тут ты вызываешь заклушку, т.е. это таки Mock-объект от Request'а r.checkCompliance.val(); } private void checkBalance(Request r) { // canWithdraw - еще одна функция, которую ты забыл мокнуть в своем тесте :) if (r.source.val().canWithdraw(r.amount)) { return; } // надеюсь предусмотрен тест сценарий при котором будет вызывать это исключение... ну да, придется как-то иначе мокать canWithdraw, но что поделать? throw new InsufficientFunds(); } private Response transaction(Request r) { // ой, еще и Transaction надо мокнуть. final var transaction = Transaction.fresh(r.source.val().id, r.destination.val().id, r.amount); // а у source и destination еще и owner надо мокнуть, нет, зная реализацию Response мы понимаем, что null там вполне прокатит, но мы же исходим из того, что реализация нам не известна ;) return new Response( transaction, new Response.Notification(r.source.val().owner, transaction.id, "withdraw"), new Response.Notification(r.destination.val().owner, transaction.id, "top up") ); } Как видишь Request придется тебе мокать целиком и полностью... ну или делать из него стаб. Тут я не хочу обсуждать как правильно это все называть. Как видим, ни от каких зависимостей ты не ушел, а реализацию тебе надо знать чтобы не мокать то, что не нужно в данном тесте. |
Сообщ.
#55
,
|
|
|
Цитата korvin @ Ага, пользуясь тем, что аргументы функции вычисляются гарантированно раньше самой функции. Даже вон do-нотацию придумали, чтобы упростить маскировку.Никто ничего не маскирует, все пишут декларативную функциональную композицию. =) Цитата korvin @ Наш мир, в первую очередь, причинно-следственный, и поэтому императивная модель куда ближе к этому миру, чем декларативная. Невозможно сварить борщ не составив последовательность действий. Даже если ты просто собираешь домик из готовых кубиков, ты не можешь начать строительство с крыши. Нет, наши физические компьютеры имеют императивную модель, а мир чисто конкурентно-событийный. |
Сообщ.
#56
,
|
|
|
Цитата Wound @ Зачем три попытки коннекта тестировать юнит тестами? Ну есть скажем требование: "в случае неудачного соединения N-раз повторить повторить попытку, после чего сообщить пользователю об ощибке". Вполне себе нормальный сценарий, который тоже можно протестировать. |
Сообщ.
#57
,
|
|
|
Цитата Fester @ Так и во втором варианте надо знать детали Какие? Цитата Fester @ Как видишь Request придется тебе мокать целиком и полностью... Не вижу, зачем? Это pure data, там нечего мокать. Если ты про canWithdraw, то это простой чистый метод, зачем его мокать? Всякие БД и прочие внешние сервисы мокают из-за сайд-эффектов и из-за того, что поднимать БД/внешний сервис для каждого теста — крайне медленно. Но можно canWithdraw вынести в Request как Lazy<Boolean>-поле, и снова мокать ничего не надо. Цитата Fester @ Как видим, ни от каких зависимостей ты не ушел Я их сделал явными и ограниченными. Цитата Fester @ а реализацию тебе надо знать чтобы не мокать то, что не нужно в данном тесте. Не надо, мне нужно знать только тип входных данных (Request) и тип выходных данных (Response), которые полностью описывают всё, что нужно. Цитата applegame @ Ага, пользуясь тем, что аргументы функции вычисляются гарантированно раньше самой функции. Что? Цитата applegame @ Даже вон do-нотацию придумали, чтобы упростить маскировку. Нет, не для этого. Цитата applegame @ Наш мир, в первую очередь, причинно-следственный, и поэтому императивная модель Не применима, т.к. позволяет менять уже существующие причинно-следственные события, когда в реальном мире они не меняются, только порождаются новые. Чисто. Функционально. Цитата applegame @ Невозможно сварить борщ не составив последовательность действий. Даже если ты просто собираешь домик из готовых кубиков, ты не можешь начать строительство с крыши. Эти аналогии вообще к чему? По-твоему функциональные программы пишут на лету, в рантайме? Программа и есть описание процесса, выполненное в той или иной нотации/модели. Цитата Fester @ Ну есть скажем требование: "в случае неудачного соединения N-раз повторить повторить попытку, после чего сообщить пользователю об ощибке". Вполне себе нормальный сценарий, который тоже можно протестировать. Ну вот смотри, D_KEY, вместо того, чтобы декомпозировать код и вынести логику retry в отдельный RetriableConnection с настраиваемой политикой, отдельно и независимо протестированную, Fester решил накостылять сомнительные тесты прям над бизнес-логикой, сохранив её сложность и зафиксировав эту сложность в спецификации (тесте). Чё-т не очень ему TDD помогает делать код/архитектуру лучше. |
Сообщ.
#58
,
|
|
|
Цитата applegame @ В жопу этот TDD, один хер реальное тестирование все равно происходит вручную в QA отделах. Да, давайте завалим QA отдел непротестированным говном, у них ведь мало работы на регрессе и проверке новой функциональности Наличие дополнительного ручного тестирования никак не отменяет необходимость автоматических тестов на разных уровнях. А TDD - это вообще про написание кода больше Цитата но в подавляющем большинстве не критических для жизни людей проектов все именно так. Да, там еще, как правило, есть разные процедуры сертификации, а иногда и системы испытаний. Это никак не отменяет важность автоматического тестирования. Я бы даже сказал наоборот. Более того, когда такое ручное тестирование находит ошибку, то по возможности нужно добавить автоматические тесты на это. Люди в следующий раз могут пропустить эту ошибку или она может не воспроизвестись, например. Прогоны в CI позволят получить более надежную систему как раз |
Сообщ.
#59
,
|
|
|
Цитата korvin @ Декларативность не подразумевает вообще никакой последовательности действий. Ты объявил какой результат желаешь получить и условия его получения и опа все волшебно само-собой сложилось. Красота.Что? Цитата korvin @ Каким образом императивная модель позволяет менять существующие события?Не применима, т.к. позволяет менять уже существующие причинно-следственные события, Цитата korvin @ Я не знаю в каком реальном мире ты живешь, но явно не в этой Вселенной. Допустим у меня есть стул и я его покрасил, в реальном мире у меня есть только покрашенный стул, в мире ФП у меня остался и покрашенный и непокрашенный. Чудеса в решете ФП переносит объекты из прошлого в настоящее. Машина времени прямо. когда в реальном мире они не меняются, только порождаются новые. Чисто. Функционально. Добавлено Цитата D_KEY @ Ну да. Я не против автоматического тестирования вообще, я скорее разочарован в TDD, никак не помогает в конечном итоге, скорее мешает. Особенно если проект не очень крупный. Наличие дополнительного ручного тестирования никак не отменяет необходимость автоматических тестов на разных уровнях. А TDD - это вообще про написание кода больше |
Сообщ.
#60
,
|
|
|
Цитата korvin @ Чё-т не очень ему TDD помогает делать код/архитектуру лучше. Так не существует никаких волшебных техник, которые бы всегда приводили к тому, что любой человек начинает все правильно декомпозировать и архитектуру хорошую делать. Вопрос в том, помогает ли TDD в этом при прочих равных. Твой тезис в том, что не помогает, есть масса сторонников TDD, которые утверждают, что им помогает. И там и там в качестве аргументов - опыт. Со стороны судить сложно, так что я хочу попробовать это на своей практике и понять, насколько это помогает мне. Кстати, обе стороны могут оказаться правыми в том смысле, что если человек не обладал навыками декомпозиции и пр., а начитавшись про TDD и начав его применять, стал об этом задумываться, то естественно он получить улучшения, а потом будет считать, что именно TDD ему помог. Хотя дело может быть исключительно в том, что он в принципе стал задумываться о тех вещах, о которых раньше не думал. |