Разработчик-тестировщик, оправдано ли?

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

Для меня как для разраба в принципе очевидно что для небольших отдельных модулей их можно написать самому. Но где находится та грань где ты уже не столько пишешь код, сколько тестируешь его вдоль и поперёк?

👍НравитсяПонравилось0
В избранноеВ избранном0
LinkedIn

Лучшие комментарии пропустить

Я работаю c Salesforce почти 6 лет. Добавление нового функционала у меня происходит путем написания кода (в 90% случаев). У Salesforce есть требование — весь код должен быть покрыт тестами минимум на 75% (по факту получается под 90% ибо нафик писать тесты и не протестить весь функицонал? А 10% остается на не проверенных catch блоках, которые ну очень редко срабатывают на боевом сервере и всего-навсего логируют ошибку). Покрытие кода тестами происходит путем написания Юнит тестов (которые у меня на практике являются Интеграционными тестами).
Помимо написания тестов, я еще свой функционал тестирую руками.
Я не считаю себя разработчик-тестировщиком. Я считаю, что разработчик должен выдать рабочий функционал. А сделать это он может, написав код и протестировав его.

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

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

Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

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

Пришел на многолетний проект без единого теста и это был тихий ужас. Пишу тесты к каждому отрефакторенному участку, пишу тесты к каждой новой функции, к каждому новому окну и так далее. Может, если работаешь над маленькими приложениями, постоянно разными и не переживаешь о поддержке — и можно обойтись без тестов. Но на долгосрочном проекте — программист обязан практиковать TDD. Самое большое преимущество наличия тестов — отсутствие боязни что-либо поломать, большая уверенность в качестве релиза.
Хорошие материалы по теме: книга Clean Code, MIT курс Advanced Software Construction (только что первая итерация на edx зкончилась), подкаст Fragmented и т.п.

А заголовок и текст точно об одном и том же? А то показалось что заголовок о QA, а текст о юнит-тестах, что разные вещи.

Юнит-тесты я лично пока что не пишу (как-то на фрилансе не встречал чтобы подымался такой вопрос вообще, всем скорее дедлайны нужны и оптимальная стоимость разработки), и отношусь вообще скептически (время разработки увеличить в 2 раза, а фронт хорошо всё равно не оттестируешь), но если кто-то будет готов платить — не против попробовать, так как потенциал в них в случае многолетнего проекта я вижу.

QA — сразу категорически нет. Видел недавно на апворке такое объявление, где искали «помощника лиду» (50%QA/50% фулстек), не знаю нашли ли. Моя точка зрения:
1) Это разные специальности, каждая из которых требует отдельных знаний и навыков. Даже фулстек — это чуть-чуть фронта и чуть-чуть бека, а делать обе части идеально просто времени не хватит, не говоря уже о том что это немного разные виды деятельности. А к этому примешать QA — и получаем мальчика на побегушках, профессионально не умеющего делать ничего.
2) Свой код тестировать хорошо невозможно. Можно только проверить соответствие формального результата работы своего кода требуемому (чем и занимаются юнит-тесты)

Изначально пост был о тестировании вообще, т.е. стоит ли писать хоть какие-то тесты. Сейчас лично мне понятно что с юнит тестами жизнь станет проще и скучнее(не надо будет ломать голову в поисках причи/фиксов непонятных багов).
Насчёт QA не всё так однозначно кмк, потому что если на фронте делаешь какой-то юзер стори (например button click -> popup rise -> close popup -> text input), то почему бы не написать небольшой скриптец на Селениуме, который будет проверять это автоматом и быстрее чем я сам? Может оно и не покажет отклонения в стилях(вернее, для этого надо больше ковырять окружение), но если кнопка поломалась/попап не закрывается — сразу будет видно.

то почему бы не написать небольшой скриптец на Селениуме
Потому что есть до 8 часов рабочего времени, и если есть выбор — то я лучше почитаю о новых фичах CSS, чем буду изучать инструментарий тестировщика (селениум). Так можно и девопса туда же, ведь что там стоит разобраться с linux, nginx, tomcat, bash, docker, vagrant, wmvare...

Это не оправдано, это маст-хэв. Осознаете это сразу, как только начнете думать не о качестве кода и о качестве продукта по-отдельности, а о качестве итогового решения.

ИМХО это показатель уровня разработчика. Вот код, вот тесты подтверждающие, что код рабочий.

Вот код, вот тесты подтверждающие, что код рабочий.
И сносочка такая:

*результаты получены в ходе лабораторных исследований и могут отличаться

Я работаю c Salesforce почти 6 лет. Добавление нового функционала у меня происходит путем написания кода (в 90% случаев). У Salesforce есть требование — весь код должен быть покрыт тестами минимум на 75% (по факту получается под 90% ибо нафик писать тесты и не протестить весь функицонал? А 10% остается на не проверенных catch блоках, которые ну очень редко срабатывают на боевом сервере и всего-навсего логируют ошибку). Покрытие кода тестами происходит путем написания Юнит тестов (которые у меня на практике являются Интеграционными тестами).
Помимо написания тестов, я еще свой функционал тестирую руками.
Я не считаю себя разработчик-тестировщиком. Я считаю, что разработчик должен выдать рабочий функционал. А сделать это он может, написав код и протестировав его.

Но где находится та грань где ты уже не столько пишешь код, сколько тестируешь его вдоль и поперёк?
Я не вижу здесь ничего зазорного или излишнего. Да, порой написание тестов занимает больше времени, чем разработка самого функионала. За то если написать новый функционал, то ты уверен, что ничего из старого не ломается, если все тесты «зеленые». Добавление нового функционала при наличае тестов просходит легче. Выявить «ломающийся» функционал из-за нового так же легче при наличае «падающих» тестов. Сразу понятно (ну, в моем случае) при каких обстоятельствах код не работает и где это исправлять.
Да, порой написание тестов занимает больше времени, чем разработка самого функионала.
Ну, имхо, если MVP начать с тестов — ними и закончится. У меня вон заказчик уже больше месяца как пропал, у меня даже недописанные изменения в репозиторий не вылиты, так как он просто перестал отвечать в чате. Такой вот стартап.

юнит тесты пишет разраб. Юнит тесты проверяют как работают КУСКИ кода, до которых тестировщику дела нет. ему в принципе все-равно используешь ты свою или чужую либу для выянения числа третьей пятницы в седьмом месяце.
Интеграционные может писать разраб, может тестер. Тестер может, потому что для этих тестов не надо знать код имплементации и используются другие средства. Например, чтобы тестить сервер нужен клиент. Клиент можно написать отдельно от сервера, и этот труд можно разделить.
Как это можно разделить? Перед тем как начать у вас должно быть ТЗ, а так же по пунктно расписано что и как должно работать. ИТ должны покрыть Каждый (!) из этих пунктов, это доказательство того что ТЗ сделано.
Теперь автоматизированые тесты, задача тестировщика. Попросту говоря это скрипт который проверит что програма запускается и отзывается как положенно. После входа должен идти экран привествия? автоматизировать и проверять.
Ручные тесты проводятся по тому же ТЗ — каждый пункт проверяется тестировщиком и подтверждает написаное с действительным. Под ответственность тестировщика. (потому когда что-то ломается всегда виноват тестировщик. Потому чаще в тестировщики берут девушек ;))

так где находится грань? между ИТ и автоматизироваными тестами. Написать ИТ тесты разраб может, но если он также проверяет то, что не описанно в ТЗ (по которым он делает ИТ), и пытается найти уязвимости — он делает работу тестировщика) все что дальше — тоже работа тестировщика. Составления ТЗ для этих тестов — работа тестировщика, но должна быть подтвержденна ПМом, мол «вы ведь этого хотите?»

Конечно правда нет наверное да конечно но это не точно завтра скажу но и может сегодня вранье пельмень камни.

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

Готов ли покупатель платить за проектирование и тестирование автомобиля? Все эти краш-тесты и кучи инженеров стоят денег. Может стоит от этого отказаться?

Дело в том, что когда человек покупает автомобиль — к нему как правило не подходит индус и не предлагает внешне точно такой же автомобиль, но за пять рупий. Кроме того если и есть индусы в автомобилестроении, автомобили производятся серийно и есть возможность узнать от других покупателей какие недостатки. Ну и наконец, есть государственные и международные стандарты, которые нужно соблюсти чтобы автомобиль можно было продавать.

В создании ПО этого всего нет. И когда от ПО не зависит чья-то жизнь, объяснить необходимость всяких тестов, увеличивающих стоимость в несколько раз бывает сложно.

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

Хотите мало фич, но все четко сделанные — пишите тесты.

Я вот ничего не хочу, я денег хочу. И заказчики тоже в-принципе.

вот вы реально от работы хотите только денег?

странный вопрос, а что еще можно хотеть? лычку крутую или чтобы программа жизни спасала?

— работать с крутыми спецами (уровня SDE Level 3 и выше)
— стать (и быть) хорошим тимлидом или архитектором, не ради лычки а как результат развития твоих технических, аналитических и человеческих качеств
— довести проект до успеха (активно участвовать в этом, думать, предлагать решения, а не делать только то что заказчик сказал)
— дойти до уровня 3 по этой матрице sijinjoseph.com/…​grammer-competency-matrix

— работать с крутыми спецами (уровня SDE Level 3 и выше)
судя по матрице это недостижимая цель у тебя, обычно у нас 0-1 синьоры встречаются
— стать (и быть) хорошим тимлидом или архитектором, не ради лычки а как результат развития твоих технических, аналитических и человеческих качеств
— дойти до уровня 3 по этой матрице sijinjoseph.com/...​grammer-competency-matrix
выбирай что-то одно, это пути в разные стороны
— довести проект до успеха (активно участвовать в этом, думать, предлагать решения, а не делать только то что заказчик сказал)
ну это обычная фигня, через раз встречается в любой компании

Хороший баблос за хорошую работу — очень хороший мотиватор. :)

Я работать начал в 2008 году, с 2010 перебиваюсь фрилансом. «Хочется переписать чтобы круто, быстро и современно, а заказчик не хочет / не может заплатить» — для меня уже не новость, а обыденность. Так чего мне хотеть?

А деньги нужны для жизни. Иначе я бы вообще не работал. Страсть к программированию легко утолить — открыл редактор и пиши что хочешь. Работа этого не даст кстати. А вообще моё увлечение в данный момент — игры. Я вот субботу и воскресенье просидел в B&S и мне норм. А до этого несколько вечеров убивал в Dishonored 2 (и время, и всех кого там можно убить). В общем-то даже если я буду понемногу заниматься всем что мне интересно — у меня времени не хватит. Тратить при этом его на чужие идеи и чужую прибыль я согласен только ради денег.

И даже когда работа мне интересна (была такая месяц назад, может ещё будет), берусь я за неё только ради денег и бросаю в тот момент, когда они заканчиваются.

К слову сказать, в этом ключе фриланс и «длинный проект» скорей всего и отличаются. На длинном проекте и заказчик местами готов переплатить за «более качественный» код. Да и сам программер чуть более заинтересованн, что когда оно пойдет в продакшен, не разгребать баги ночами под палкой менеджера.

Вы шо, Чак Норрис?
Не, ну если Вы визитки клепаете, то понятно откуда такие вопросы

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

Может быть и не легаси, а вполне себе свежачок, но как тут справедливо заметили — без тестов он быстро превратится в комок унылого спагетти, потянув котрое в одном месте можно успешно разломать всю конструкцию.
В этом у меня сомнений не было.
Однако я думал что для написания/выполнения тестов должны быть специальные люди, которые обучены именно находить и описывать баги. Они коммуницируют с разрабами и сообща выдают хороший продукт.
Такое разделение не потому что с погромиста корона упадёт копаться в тестовом окружении, а потому что вроде как разделение труда и специализация — двигатель прогресса :) И (я так полагал), что команда 3 разработчика + 1 тестер будет эффективнее чем 4 разработчика, пишущих тесты к своему коду.

эффективнее чем 4 разработчика, пишущих тесты к своему коду.

Юнит-тесты (если мы о них) не должны писаться к коду. Они должны писаться к спецификации/декомпозиции системы (т.е. публичным, реже непубличным интерфейсам).

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

для написания/выполнения тестов должны быть специальные люди
Еще раз. Оцените время на цикл (и дайте примерную разбивку по стадиям): написал код — нашли баги — поправили баги — нашли новые — поправили.
И (я так полагал), что команда 3 разработчика + 1 тестер будет эффективнее чем 4 разработчика, пишущих тесты к своему коду.
Почитайте что-то про параллельные вычислени. Ваша схема 3+1 будет работать только -если тестировшик будет выполнять свою работу в 3 раза быстрее чем программисты- если затраты времени на тестирование будут в 3 раза меньше чем на разработку. Подумайте как этого можно достигнуть.
Однако я думал что для написания/выполнения тестов должны быть специальные люди, которые обучены именно находить и описывать баги. Они коммуницируют с разрабами и сообща выдают хороший продукт.
Да просто тесты разные бывают.
Юнит тесты пишет сам разраб чтобы проверить не нагородил ли он херни и не разламал ли существующий код.
Дальше есть интеграционные тесты, автоматические тесты и ручное тестирование.
Интеграционные тесты нужны для проверки как код интегрируеться с остальными модулями и кусками инфраструктуры. Как правило за этим меньше следят, тоже писать вроде как программистам нужно, но на моей практике — самый мало описанный, самый из рук вон плохо поставленный процесс.
Ручное тестирование — это проверка (в ручном режиме) развернутой системы на предмет работоспосбности\соотвецтвования требованиям. Сейчас мода не делать этого вообще, либо надеяться что сами программеры это както сдалают (чего они никогда не сделают). Имхо, мода плохая, нынче 90% багов в продакшене из за отсуствия нормального тестирования.
Автоматическое тестирование, тожесамое что тестирование но автоматизированое. Типа нужен отдельный человек. Я с такими типом тестирования никогда не работал, потому отношение у меня к нему недоверчивое.

Возможно я ошибаюсь, но кажется мы говорим о разного рода тестах. Юнит тесты не имеют никакого тестового окружения и должны запускаться и выполняться максимально быстро. В идеале после каждого изменения продакшн кода ты должен запустить тест для этого класса, который максимально быстро покажет что ты сломал. Именно поэтому их должен писать разработчик. Специально обученные люди (видимо речь о QA Automation?) пишут более высокоуровневые тесты, фидбек от которых разработчик получит намного позже того, как пушнет свои изменения и тем самым сделает их доступными для всей команды.

Да, сам вопрос был написан довольно размыто.
То что пока удалось понять — оптимальным будет использование сочетания юнит-тестов для низкоуровнего(относительно) тестирования логики кода и BDD для высокоуровнего тестирования работоспособности интерфейса и user flows.
Осталось только понять как это делать на фронтенде. То есть, допустим, мне надо навесить класс на элемент при определённых условиях. Я могу тестами проверить, навешивается ли этот класс. Но эти же условия могут привести к тому что у родителя этого элемента навесится другой класс и в итоге пользователь увидит непонятно что.
Пока что есть идея делать diff скриншотов, посмотрим как оно будет работать.

Мне кажется, что все эти разговоры, что нет времени, денег, тестировщиков, одобрения заказчика etc писать тесты — это симптомы. Тесты не пишутся, потому что их слишком сложно или вообще невозможно написать. А причина — плохая компоновка кода. Даже если просто держать в уме «а как я этот метод буду тестировать?» — это уже очень сильно помогает. И Arrange-Act-Assert не должен особо выходить за рамки трёх строчек.

Если это не так, то не с тестом проблема, а сигнал, что код плохой.

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

public void congrats() {
    Date today = new Date();
    if (today.getMonth() == 1 && today.getDay() == 1) {
        System.out.println("Happy New Year!");
     }
}

Здесь ошибка. И она не в том, что не использован Calendar, а в том, что непонятно, как этот метод протестировать. И если исправлять баг, то, опять же, нет уверенности, что исправление верное. Главная проблема в сигнатуре метода, компоновке класса, а меньшая — что реализация подкачала.

Вот только если логику нигде не требуется понимать за пределами кода, если сама задача congrats вполне абстрагируемая, и решаемая по принципу «сделай и забудь» — так и надо!

На кой здесь тесты, если прочитать код занимает секунды, равно как и оценить что он делает? Я больше скажу, подобный код даже абстрагировать в функцию противопоказано, его следует просто скопипастить туда где он требуется.

Оцените сами, что проще: прочитать 2 строчки кода, или разобраться в месте вызова, что делает congrats?
Date d = new Date();
if(d.getMonth().equals(1) && d.getDay().equals(1)) Logger.log(Level.INFO, «Happy New Year!»);

Как видите, при правильном рефакторинге и тестировать нечего. Временные данные и только. Тестировать нужно месте РАЗРЫВА логики, там где происходит переход к абстракции. И чем таких мест меньше, чем они более предсказуемы — тем проще и тестирование, и поддержка, и затраты на риски ошибок.

Date d = new Date();
if(d.getMonth().equals(1) && d.getDay().equals(1)) Logger.log(Level.INFO, «Happy New Year!»);
Как видите, при правильном рефакторинге и тестировать нечего.
1) Ну да, ваш-то код вообще не компилируется. Логично что даже не компилируется, не вожет вызвать баг на проде :)
2) Тестировать в коде выше таки есть что (там есть баг). :)

С одной стороны, я с вашими мудрыми словами, что тестировать нужно разрывы логики согласен. Только как практически это делать-то?

Вот вы код отрефакторили, а баг остался. 1-ого января поздравления не отправятся, в лог ничего не выведется, и ясности почему это произошло — не прибавилось.

Ну здесь ХЗ, я согласен с тем что сам так никогда не пишу. В моём коде дата была бы приведена к строке нужным форматом, и сравнивалась с нужной строкой. Во-первых, потому что сам код так становится понятней мне самому при беглом просмотре. Во-вторых, это проще чем вспоминать с 0 или с 1 нумеруются дни и месяцы. Помню что в JavaScript была подобная «не баг, а фича» — и вероятнее всего существует до сих пор «для совместимости».

Я лишь показал подход: код должен быть читаемым. Настолько читаемым, чтобы понимать есть ли там баг — не было нужды. Если это непонятно, код надо рефакторить полюбому.

Вы сейчас говорите про реализацию. Сравнение со строкой тоже может привести к проблемам, при локализации например. Никто не будет спорить, что код должен быть максимально читаемым. Однако, читаемость не гарантирует хорошей компоновки/архитектуры. Пример выше вовсе не про то, с какого числа нумеруются месяцы.

Главная проблема в сигнатуре метода, компоновке класса, а меньшая — что реализация подкачала.

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

Поэтому я ещё раз повторюсь

Даже если просто держать в уме «а как я этот метод буду тестировать?» — это уже очень сильно помогает.
Даже если просто держать в уме «а как я этот метод буду тестировать?» — это уже очень сильно помогает.
В том весь и смысл, чтобы держать в уме как можно меньше. Чтобы не пытаться анализировать то, что не сломалось.

Попробую сказать иначе: реализацию крайне выгодно держать доступной не по имени, не по пути к файлам, не по логике вызовов. А просто в том месте, где её будут искать. И ограничить исключительно этим местом.

То же самое с тестами. Они должны быть написаны и находиться именно в том месте, где они будут понятны. Где нет необходимости знать что-то ещё. Если же для понимания теста нужно понимать код — это ПЛОХОЙ тест, от него нужно избавится как только будет достигнута первая альфа-версия. Всё что делает код — должно быть написано в коде, и нигде больше. Если нужно защититься от изменения кода — необходимо делать правила, доступы, защиту версий от человека, бэкапы — но никак не дублирование той же логики в другом коде.

Код невозможно покрыть тестами. Только сымитировать это покрытие. Притом покрыть те места, которые никогда не ломаются и в которых маловероятны ошибки.

Типичный пример: в PHP передать переменную в цикл по ссылке, или объявить ссылкой в самом цикле и не уничтожить после. Как ты проверишь эту ошибку, не зная заранее куда положил грабли? А никак. Нужно больше внимания уделить коду, а не писать к дедлайну 100500 тестов, высунув язык.

Я уже молчу, что основная работа программирования — где-то что-то взять и куда-то положить. И самый правильный способ это сделать — напрямую. Задача «а как потом это тестировать» потребует абстракции, такой чтобы и точка взятия, и точка куда ложить — была доступна извне, и понятно что это и как.

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

Ещё хуже, когда какую-то неочевидную вещь приходится выносить в настройки — и всё ради тестирования.

Извините, я вас плохо понимаю.

Попробую сказать иначе: реализацию крайне выгодно держать доступной не по имени, не по пути к файлам, не по логике вызовов. А просто в том месте, где её будут искать. И ограничить исключительно этим местом.

Есть класс/интерфейс, есть его реализация, одна или несколько. Вы о каком поиске?

То же самое с тестами. Они должны быть написаны и находиться именно в том месте, где они будут понятны.

Опять же, есть конвенция: имя класса Foo, имя теста — FooTest. В том же пакете.

Если же для понимания теста нужно понимать код — это ПЛОХОЙ тест

А может это плохой код? Тест должен писаться просто — AAA или GWT. Если это невозможно, имхо, проблема в сложном методе.

Всё что делает код — должно быть написано в коде, и нигде больше. Если нужно защититься от изменения кода — необходимо делать правила, доступы, защиту версий от человека, бэкапы — но никак не дублирование той же логики в другом коде.

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

И самый правильный способ это сделать — напрямую. Задача «а как потом это тестировать» потребует абстракции, такой чтобы и точка взятия, и точка куда ложить — была доступна извне, и понятно что это и как.

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

public void congrats() {
    Date today = new Date();
    if (today.getMonth() == 1 && today.getDay() == 1) {
        System.out.println("Happy New Year!");
     }
}

и

@AllArgsConstructor
class Congrats {
    private final Date date;

    public String toString() {
        if (date.getMonth() == 1 && date.getDay() == 1) {
            return "Happy New Year!";
         }
         return "";
    }
}

...

System.out.println(new Congrats(new Date()));

Да, кода стало на пару строчек больше, и то, спасибо Lombok-у, а то Java не особо лаконична.
Да, баг всё ещё присутствует, но когда на него наткнутся, понятно, как написать тест, исправить, и быть уверенным в исправлении.
Да, на первый взгляд проще «напрямую», но тогда нет ни в чём уверенности, кроме как в полной регрессии на каждый чих.

В местах разрыва или естественных абстракций — сущности легко объяснимы. А вот чтобы объяснить доступ к неочевидным временным частям целого объекта в неочевидной стадии — нужно очень постараться.

Если не затруднит, может пример кода? Не могу понять, о чём идёт речь. Вы про инкапсуляцию, композицию и иммутабельность?

Да, кода стало на пару строчек больше
Боже, да лучше баг, чем такой код повсюду. Это printDateEnterprise какой-то. Раздули код ради тестирования минорного функционала.

Ну, насчёт «лучше баг» — не могу согласиться. Или можно с НГ и в другой день поздравить, пользователи перебьются? :-) У нас, например, релизное окно — раз в два месяца, поэтому «быстренько подфиксить на проде» не работает.

И, главное, как быть с сопровождением кода?

Пример выше — вообще не про баг, а иллюстрация проблемы,

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

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

Что мне не нравится — это именно тенденция усложнять архитектуру, которая и так непростая в реальных проектах
чет мне кажется ты говоришь о том самом типе архитектуры, которая называется «так исторически сложилось»

А мне кажется что каждый второй тут хелловорды пишет с ООП как по книжке :)

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

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

На туду-листах все показывают TDD, но туду-листы никому не нужны.

Мне видится, что тесты не пишут не потому, что не хотят, а потому что на первоначальный вариант метода — простой, понятный, читаемый и с багом — тест написать нельзя.

Далее, я считаю, что хорошая архитектура и покрытие тестами — это не противоположные направления, а одно.

юнит-тест — вовсе не обязательно что он проверяет все возможные сценарии поломки

Мне кажется, что цели юнит-теста такие:
1) даже не написанным позитивно влиять на архитектуру. Если ветвлений нет, возможно логгирования будет достаточно;
2) зафиксировать для регрессии/возможного будущего рефакторинга positive flow;
3) в случае рисков и/или багов добавлять negative flows;

на первоначальный вариант метода — простой, понятный, читаемый и с багом — тест написать нельзя.
А его вообще нужно писать? Зачем? Это три строки кода без зависимостей от других частей проекта. А юнит-тест пишется на будущее. Что может в будущем сломаться что эти 3 строки перестанут работать?

Я нигде не говорил, что нужно написать или не написать тест на этот метод. Но всячески подчёркивал, что кмк полезно задумываться, как же его тестировать. И таким образом получить сигнал, что в теле метода есть проблема компоновки.

А если метод написан плохо и именно по этой причине на него нет теста, то баги там появятся рано или поздно при дальнейшем развитии проекта.

Обратите внимание, у вашей функции стояла задача: «Поздравить пользователя, если сегодня новый год». Эта задача не предусматривает передачи функции параметров. Сегодня есть сегодня. А вам понадобился параметр только из-за теста, потому что иначе никак не убедишься что функция сработает.

Окей, а вы что предлагаете: подождать до Нового года и посмотреть сработает или не сработает?

Перевести время на машине разработчика и посмотреть сработает или нет. Именно так проверяется что оно работает сейчас. А если оно работает сейчас, то нет никаких причин ему не работать в будущем. Это код, который использует только стандартные библиотеки, с чего ему сломаться?

Я ожидал такого ответа.

Во-первых, вы правы — ручное (ну или автоматическое) тестирование действительно превыше всего.

И если юнит- и интеграционных тестов нет, то только оно и остаётся. А оно довольно медленное и дорогое.

Код может сломаться при изменениях, например, другие праздники добавятся.

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

Поэтому я повторюсь.

Заранее усложнять код под будущие изменения — плохо, а если даже не писать тесты, но думать о них — хорошо.

А если оно работает сейчас, то нет никаких причин ему не работать в будущем.
ахахахахахаха

Ну что ха-ха? Из стандартной библиотеки какой-то версии удалят Date? Или if станет работать по-другому?

Извините, объясняю на пальцах.

Есть метод, он что-то делает. Например, отправляет поздравления на НГ. Теста на него нет, потому что непонятно, как задачу разбить на классы и методы.

Поэтому она проверяет ручным тестированием с переводом даты на машине разработчика.

Потом проект развивается, например, добавляют новые праздники и поздравления через смс. Код изменяется. Вручную тестируется обычно новый функционал. Регрессию — полное перетестирование всех старых фич — делать долго и дорого. Поэтому не факт, что рассылка на НГ будет перепроверена.

Может через год она и отработает нормально. А может и нет.

Если вы умеете добавлять/изменять/развивать код, гарантируя сохранность старого функционала, так и скажите.

Извините, объясняю на пальцах.
Не нужно объяснять в десятый раз, я с первого понял о чем вы хотели сказать. Проблема как раз в том, что вы объясняете на пальцах, и ещё 1000 авторов кроме вас объясняет на пальцах.

Таких примеров масса на разные темы, не только TDD, когда сложную технологию пытаются показать на простом примере, на который только и хочется ответить «и чё?». Не нужно сложные вещи пояснять на простых примерах, это не работает.

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

Я понимаю ваше возмущение.

Вот есть сложный метод, поэтому на него нет теста.
Говорите, его надо разбить на части? Окей, а как?

Шаблоны проектирования? Ну, на калькуляторах и туду-листах все эти фабрики и стратегии понятны. А в реальном проекте как быть?

Много общих слов, и мало конкретики? Так я пытался свои слова конкретикой подкрепить.

1. Нет теста? — сигнал, что плохой метод.
2. В чём причина? — на мой взгляд, в new Date() в теле метода.
3. Как исправить? — не создавать объект внутри, передавать его извне.
4. Что дальше? — продолжаем изучать ООП.

Вот потом можно и подумать о юнит-тесте,

Уже отвечал.

Я нигде не говорил, что нужно написать или не написать тест на этот метод. Но всячески подчёркивал, что кмк полезно задумываться, как же его тестировать.
Ну, на калькуляторах и туду-листах все эти фабрики и стратегии понятны. А в реальном проекте как быть?
Уверен, об этом каждый день задумываются миллионы разработчиков. Потому что на простом вроде всё понятно, а что делать с большой спецификацией или сложным кодом перед глазами — непонятно.

Понимаете, я в последнее время занимаюсь фронтом (ember.js). И вот в гайде тоже где-то упоминались тесты. На примере тестирования рендеринга простого компонента. Но что делать, если компонент зависит от сервиса, который в свою очередь зависит от нескольких сервисов и меняет состояние чего-либо в приложении, я до сих пор не знаю. По текущему гайду нужно такой сервис заменять в тесте. То есть в сложных случаях продублировать код. И что я вижу в примере? Компонент, в шаблоне которого одна строка:

Pretty Color: {{name}}

И вот этот пример просто ну ни о чём. Такой компонент можно не писать вообще, это одна строка в шаблоне. Для форматирования разных данных я вообще использую хелперы, потому что преобразовать одно значение в другое — задача именно хелпера, а не компонента (последний обязательно создает как минимум один элемент в DOM и имеет свой жизненный цикл). А что-то сложнее тестировать тоже сложнее. И как вишенка на торте — тест не говорит ничего о внешнем виде отрендереного компонента и все может поломаться у какого-нибудь пользователя из-за того что у него другая ОС (не шучу, chrome под osx и под windows работают немного по-разному).

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

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

то что ты пишешь это тоже самое как люди только узнавшие о ттд спрашивают как протестировать приватный метод

та самая реальная работа должна состоять из тех самый простых примеров, которые показывают на пальцах
Если бы все так думали, был бы у нас только инстаграм с твиттером. ФБ — уже слишком сложно, говнокод получится нетестируемый.

еще скажи что юнит-тестированием никто не занимается в крупных проектах :)

Помню что в JavaScript была подобная «не баг, а фича»
Живет и здравствует. Месяцы с нуля, если мне не изменяет память. Хорошо что есть moment.js, который избавляет от необходимости с этим приколом сталкиваться почти полностью.
Вот только если логику нигде не требуется понимать за пределами кода, если сама задача congrats вполне абстрагируемая, и решаемая по принципу «сделай и забудь» — так и надо!
так вообще со всем кодом можно поступить, разница только где именно будет нетестируемый кусок кода, который работал, когда его писали
На кой здесь тесты, если прочитать код занимает секунды, равно как и оценить что он делает? Я больше скажу, подобный код даже абстрагировать в функцию противопоказано, его следует просто скопипастить туда где он требуется.
а ты когда-нибудь участвовал в написании хотя бы среднего по размерам приложения? то что ты говоришь применимо только к маленьким проектам, на средних всегда ведет к тому что регрессия занимает недели, а работа программиста это багофикс 80% времени
Оцените сами, что проще: прочитать 2 строчки кода, или разобраться в месте вызова, что делает congrats?
точно знаю что прочитать название метода проще, чем блок кода произвольного размера
Как видите, при правильном рефакторинге и тестировать нечего
во-первых, это не рефакторинг, во-вторых, как это нечего тестировать? тут 5 вызовов методов, 2 зависимости и 4 магических значения, то что ты посчитал этот блок логики несущественным не значит что его можно не тестировать

Дело не в магических значениях. А в замыкании объектов ЧЕЛОВЕЧЕСКОЙ логики как можно ближе друг другу. Если есть тесты — они крайне желательны прямо в коде.

Самое сложное в тестировании — то, что оно ВНЕШНЕЕ. То есть чтобы протестить, нужно ЗНАТЬ код. А чтобы поменять код — нужно ЗНАТЬ тест, и разобраться со всеми тестами которые его проверяют. Результат? Как минимум удвоение времени, хотя я дал бы оценку затрат в 5 и более раз.

Тестирование необходимо. Но тестироваться должно то, что проще проверить сравнением результата чем разбором кода. Как правило, это места разрыва или «сшивки» кода. А вот проверять что-то «по живому», там где код является единым блоком — выгодно только при написании. А потом даже уже существующие тесты начинают мешать своим присутствием, их лучше выбросить. Просто потому что ЛЮБАЯ избыточная информация — удорожает дело.
______
Название метода? Это едва ли не самое сложное в программировании, назвать так чтобы было понятно.

Дело не в магических значениях. А в замыкании объектов ЧЕЛОВЕЧЕСКОЙ логики как можно ближе друг другу. Если есть тесты — они крайне желательны прямо в коде.
как бы в начале 2000 стало понятно что программирование на утверждениях неудобное, даже когда утверждения начали заворачивать в директивы препроцессора чтобы они не выполнялись на релизном билде оказалось что они сильно загромождают код и хоть пользу они и приносят вклад во вред тоже весьма существенный
Самое сложное в тестировании — то, что оно ВНЕШНЕЕ. То есть чтобы протестить, нужно ЗНАТЬ код. А чтобы поменять код — нужно ЗНАТЬ тест, и разобраться со всеми тестами которые его проверяют. Результат? Как минимум удвоение времени, хотя я дал бы оценку затрат в 5 и более раз.
то у тебя просто пробел существенный в этой области, в самом плохом случае на 30% увеличится время выполнения, обычно на 10%, а бенефит в качестве практически отсутствие багофикса покрывает затраты времени
Тестирование необходимо. Но тестироваться должно то, что проще проверить сравнением результата чем разбором кода.
а как насчет регрессии? сначала она дни, потом недели, потом месяца? хотя соглашусь, это действительно проще, только чем дальше, тем дороже
А вот проверять что-то «по живому», там где код является единым блоком — выгодно только при написании.
при написании проверять новый код это только в ттд, юнит тесты это инвестиция в будущее, а не в настоящее
А потом даже уже существующие тесты начинают мешать своим присутствием, их лучше выбросить. Просто потому что ЛЮБАЯ избыточная информация — удорожает дело.
нарушение ооп и оод, когда так случается, если без нарушения писать не получается, то, наверное, юнит-тесты стоит отложить и разбавить коллектив настоящими программистами, а то те люди, который просто пишут код, который просто скомпилился и работает вопреки до добра не доведут
Название метода? Это едва ли не самое сложное в программировании, назвать так чтобы было понятно.
согласен, но «та чёт сложно было» это не оправдание плохо сделанной работе
как бы в начале 2000 стало понятно что программирование на утверждениях неудобное, даже когда утверждения начали заворачивать в директивы препроцессора чтобы они не выполнялись на релизном билде оказалось что они сильно загромождают код и хоть пользу они и приносят вклад во вред тоже весьма существенный

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

Так что, это у вас слабый тезис против ассертов. :)

Другое дело, что индустрия пошла в бурный рост — а это значит реализация крупных проектов средствами толп дешёвых/низкоквалифицированных кодерков. В такой сутуации, когда «кодерки» нагромождают кучи кода, с кучей копи-пасты, при этом ничего не проверяют в коде явно — весь код приходится проверять опосредованно. Обязательными юнит-тестами.

Кроме того, юнит-тесты позволяют верифицировать код без запуска продукта/подсистемы и работы с ним (продуктом). Тоже полезное свойство.

Смысл бюрократии — в самих ритуалах. Целуйте жопу Хэнка, и получите миллион долларов.

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

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

Тесты — такой же код. И при их написании говнокод фактически считается «хорошей практикой». И вот как раз их пишут просто чтобы формально покрыть код. А на самом деле — чтобы просто добавить тонну говна в проект, и это говно придётся поддерживать и костылить. Разумеется, с увеличением количества ошибок. Потому что покрыть100% кода — не означает покрыть 100% входных данных.

А хорошая практика — это покрывать входные данные прямо в коде. И да, при случае проверить код тестом. Один раз. А потом прибить тест, чтобы недобавлял мусора в стабильный код. Когда код понадобится переписать (что маловероятно) — написать тест по-новой будет куда быстрее, чем найти существуюший. Особенно если переписывать и не надо, а надо подправить багу, например криво поставленное ТЗ.

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

----------------
Повышенный расход времени не на выполнение, а на понимание кода. Человеческий фактор — расходная статья № 0 в нашем деле. Количество мусора катастрофически сказывается на жизнеспособности проекта. Фактически, доброкачественная опухоль. Которая может стать злокачественной, когда дедлайн и говнокодеры сделают своё дело.

как докажешь что твой код ничего не сломал в другом месте? как считать импакт?
как быть с этим:

следует просто скопипастить туда где он требуется.
«сделай и забудь» — так и надо
?

Никак. Принципиально код не должен ничего ломать в другом месте. А другие места принципиально не должны быть доступны для изменения (без соответствующих танцев с бубном).

ЕСЛИ же код используется в другом месте, то есть имеет место разрыв — то и тест должен находиться по ту сторону разрыва. То есть использующий код имеет право протестить мой. А я соответственно должен предоставить интерфейсы для безопасного теста, и даже описать их в документации.

Притом это важно не столько для работы, сколько для разработки. Занимаясь дебагом своего кода, разраб должен быть уверен что проблема не на моей стороне. Особенно если этот разраб за океаном. Соответственно, в его тесте будет вызов моего. Но я акцентирую: моего БОЕВОГО кода, а не тестов. А тестов — только в случае специального ТЗ, при котором тесты становятся открыты, под них заводятся полномочия доступа и вероятно права в системе.

Вы какие-то странные вещи говорите.

То есть чтобы протестить, нужно ЗНАТЬ код. А чтобы поменять код — нужно ЗНАТЬ тест,

Если это так — это плохой признак. Сигнал о сложном методе, для тестирования которого нужны mocks == знания о том, что происходит внутри.

Тесты — такой же код. И при их написании говнокод фактически считается «хорошей практикой».

Зачем же так?

>>И при их написании говнокод фактически считается «хорошей практикой».
>Зачем же так?

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

Так что, в этом пункте — оратор выше прав.

Вы тоже ужасы рассказываете, хотя, не буду отрицать, я с ними знаком :-)

Код, код нужно писать так, чтобы

Arrange-Act-Assert не особо выходил за рамки трёх строчек.

Если большой/сложный/с ответвлениями/повторяющийся сетап — это в коде проблема, метод слишком сложный. Это всё должно один раз тестироваться, а не повторяться для разных тестов.

Arrange-Act-Assert не особо выходил за рамки трёх строчек.

Во-во. Скажем, в «индусском варианте» это выглядит так, что для структуры c 4-мя полями — пишется 4 одинаковых теста (сетап/акт), в каждом из которых проверяется по полю структуры. :)

Попробую объяснить. Если для структуры написан «индусский» тест на сеттеры-геттеры, а на «процедуры» тестов нет, потому что слишком сложно, или тесты с копи-пастой сетапа, ЗНАНИЯМИ о коде и прочими страшными вещами, перечислеными выше...

... то не в тестах проблема. Вернее, она, естественно, есть, но это следствие и симптом того, что

Проблема в компоновке кода. Он неправильно разбит по классам, в нём слишком сложные методы, не применены шаблоны проектирования и т.д. Этот код будет, скорее всего, будет сложно переиспользовать, модифицировать при изменяющихся требованиях и т.д. Даже если вдруг реализация вначале была и без багов.

Я выше приводил пример с датой и поздравлением с Новым годом. Даже в таком коротком и простом коде ошибка, на мой взгляд, из-за создания нового объекта — даты — в теле метода.

Кто-то изобрел машину времени и в теме зазвучали голоса из начала 2000?

И пишутся тесты, которые нифига реально не тестируют, но графики растут

И пишутся тесты, которые нифига реально не тестируют, но графики растут
Мутационное тестирование

Разработчику нет смысла быть тестировщиком — т.к. для тестирования (включая, написание юнит-тестов по спецификации) достаточно людей с меньшей квалификацией/меньшей оплатой, чем для разработки.

Обширные юнит-тесты служат средством контроля кода в средних/крупных проектах, с кучей низкоквалифицированных кодерков.
Если проектом занимаются квалифицированные люди, с высоким уровнем повторного использования кода — на юнит-тесты можно забить болт (при условии, обширных проверок по коду/логгинга + тима «мануальщиков» или продвинутых средств интеграционного тестирования).

Не согласен по форме — «кодерки», «забить болт», но согласен по сути — если код скомпонован правильно и есть логгирование, то очевидные тесты можно и не писать.

Разработчику нет смысла быть тестировщиком — т.к. для тестирования (включая, написание юнит-тестов по спецификации) достаточно людей с меньшей квалификацией/меньшей оплатой, чем для разработки.
Квалификация не меньшая, квалификация другая. Как бывает гавно-код так бывают и гавно-тесты.

Сидеть под интерфейсы юнит-тесты колбасить, поглядывая в спецификацию — особых талантов не надо. Хватит и образования 2 классов ПТУ.

Коллега, ну мы же программисты всё таки. Давайте не будем «колбасить», пожалуйста.
Посмотрите в сторону Parameterized, если уж есть необходимость проверять интерфейс на большом количестве значений.

Как вы относитесь к написанию тестов для своего кода? В каких случаях это имеет смысл?
Понятно что без тестов не обойтись, но кто и в каком объёме их должен выполнять?
Задача разработчика выдать __работающую__ систему (в случае с джунами фичу).
Написали вы какой-то код. Как вы собираетесь гарантировать работоспособность? (Скажу по секрету: ТДД/БДД — самы дешевый способ :) )
— готов ли заказчик вкладывать ресурсы в такой подход(по сути — платить девелоперу за тестирование, а также нанимать больше людей чтобы не сдвигать сроки);
Нет не готов, поэтому првиль спросить: готовы ли вы выгребать и овертаймить или искать новую работу каждый второй спринт?
— хочет ли этим заниматься сам дев (есть мнение что лучше подучить что-то новое «по профилю», чем разбираться с тестовым окружением и базовой теорией);
Снова же вопрос: __хочет__ ли работодатель платить вам деньги? есть мнение что лучше их потратить «по профилю».
— размер проекта, потому что на небольших проектах оверхед будет больше чем полученная отдача;
Таки да если проект 1-2 недели с внедрением, то наверное не стоит.
— степень проработанности проекта(писать тесты на модули без чётких критериев «хорошо сделано» и «плохо сделано» это боль).
Что именно вы подразумиваете под «проработаностью»? Если бизнес-требования, то вопрос как вы их будете менять? Снова же как вы гарантируете что своими изменениями не поламали другую фичу?
---
Основная проблема с тестировщиками с точки зрения разработчика — это время отклика. Как-то сложно обеспечить прогонку ручных тестов за менее 30 мин :)
Скажу по секрету: ТДД/БДД — самы дешевый способ :)
Я думал что это каждый раз индивидуально. То есть существуют случаи когда взять тестировщика и оставить разрабам только написание кода таки дешевле.
Нет не готов, поэтому првиль спросить: готовы ли вы выгребать и овертаймить или искать новую работу каждый второй спринт?
Как показывает практика, процесс накопления технического долга до критической точки(когда внедрение новых фич становится практически невозможным) может занимать длительное время, зависящее от квалификации разработчиков. И всё это время заказчик будет доволен, ему будет тяжело обосновать выделение ресурсов на TDD или даже просто на тестирование, если изначально этого не догадались сделать.
есть мнение что лучше их потратить «по профилю».
Ну вот в том то и дело что это мнение преобладающее. Тесты или выполняются специально обученными людьми или не пишутся вообще.
Что именно вы подразумиваете под «проработаностью»? Если бизнес-требования, то вопрос как вы их будете менять?
Под проработанностью как раз имеются в виду требования заказчика. Если они сформированы недостаточно четко то код ещё можно как-то написать(и получить на выходе хз), но вот тесты к этому коду превращаются в «фантазии свободного художника».
А менять бизнес требования можно, надо только понимать что в текущем виде они недостаточно описаны и обладать умением донести это до заказчика.
до критической точки(когда внедрение новых фич становится практически невозможным) может занимать длительное время
Ну да лет 10 проедет.
А вот капасити спринта начнет падать на 10+% уже через 4-6 спринтов. А после выхода в прод, еще на 20-30%. А заказчик кстати привык к 10 фич за спринт и не понимает почему это команда делает всего 6-8.
Если они сформированы недостаточно четко то код ещё можно как-то написать(и получить на выходе хз), но вот тесты к этому коду превращаются в «фантазии свободного художника».
Почитайте про ТДД и больше про БДД. Идея в том что вы сначала определяетесь со сценарием, а потом его реализовываете. То есть тесты у вас не могут быть непонятнее чем код. Но если писать тесты, шоб отипались, то тут начинаются проблемы.
А менять бизнес требования можно, надо только понимать что в текущем виде они недостаточно описаны и обладать умением донести это до заказчика.
Что значит недостаточно описаны?
БДД как раз помогает их описать.

Вопрос в чем? Если в названии топика, то переведите просто на английский и будет ответ:
Software engineer in test.

Если в коде много косяков, которые уже влом выискивать и исправлять, то так и скажи.

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

Хотя встречаются тут любители TDD.

Лучше быть богатым и здоровым, чем бедным и больным. Хороший разработчик, даже работая в команде, просто не делает баги, поэтому и не нуждается в тестах. Извините.

Хороший разработчик ДЕЛАЕТ баги. Но они не настолько примитивны, чтобы быть пойманными мифическим «100% покрытием кода». И чем больше времени он тратит на бюрократическую нюхню — тем больше шансов допустить логические ошибки по коду — когда идею нужно сохранить одновременно во многих местах, и помнить что это нужно сделать.

Основная ошибка разрабов, и она же самая трудноуловимая — что на этапе разработки нужно держать в голове места, где код разрушен, помнить что именно там не так работает или не работает вовсе — и суметь завершить запись до того как какой-нибудь долболюб-менеджер захочет «срочно» поговорить по телефону или другим способом проверить мозг.

Проблема в том, что у мозга память ассоциативная, там невозможно хранить дубликаты. Равно как и делать 100% стирание старого. И если с первого раза задача делалась за час, то после того как она была оборвана на середине — доделать её займёт неделю, если задача касалась каких-то изменений. Почему так: то что уже сделано — забыто, и вспомнить всё нереально. Хороший разработчик знает, что нужно просто пройти тем же путём с нуля, и так сократить время и баги (но устранить на 100% риск невозможно). Плохой — будет пытаться всё доделать или бросит и начнёт с нуля, а чаще и то и другое.

Давай, расскажи, как тесты помогут найти то, о чём компьютер вообще не догадывается? Чтобы тест сработал, нужно один раз правильно написать код. А потом ещё и правильно тест. Те же задачи, которые легко тестируются — настолько же легко пишутся правильно с первого раза.

Согласен, есть класс задач, которые структурно сложные, но по логике простые. Типа, переведение данных из одного формата/источника в другой. Но как раз они тестируются не только лишь всеми, и про это даже напоминать не нужно. Просто потому что риск ошибиться огромный: для мозга данные не имеют смысла, а вот для компьютера логика примитивна: нужно просто сравнить большой объём данных.

Но такие задачи составляют очень малый объём разработки, менее 20%. А стремление распространить практику тестирования на всё — убивает все остальные задачи, переводя ресурсы на бюрократию. Был свидетелем одного проекта, где тестирование форматов данных добавили прямо в продакшен и оно выполнялось на каждой передаче (а там хайлоад, между прочим) — это назвали супер-фичей по повышению надёжности.

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

На мой взгляд, шаблоны проектирования в том числе и про то, как порезать код по классам так, чтобы и ответственность разделить, и протестировать легко можно было. Даже если и не написать тесты.

Если не возражаете, чтобы не повторяться dou.ua/…​ign=reply-comment#1095064

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

На мой взгляд, если практика тестирования «убивает» — это сигнал о плохом коде, а не о бюрократии.

Без тестов код получается плохой, архитектура страдает

Почитайте. Как раз про покрытие каждой строки пишут.

One-to-One Correspondence.
If you’ve been following me for any length of time you know that I describe TDD using three laws. These laws force you to write your tests and your production code simultaneously, virtually line by line. One line of test, followed by one line of production code, around, and around and around. If you’ve never seen or experienced this, you might want to watch this video.

Most people who are new to TDD, and the three laws, end up writing tests that look like the diagram on the left. They create a kind of one-to-one correspondence between the production code and the test code. For example, they may create a test class for every production code class. They may create test methods for every production code method.

Of course this makes sense, at first. After all, the goal of any test suite is to test the elements of the system. Why wouldn’t you create tests that had a one-to-one correspondence with those elements? Why wouldn’t you create a test class for each class, and a set of test methods for each method? Wouldn’t that be the correct solution?

And, indeed, most of the books, articles, and demonstrations of TDD show precisely that approach. They show tests that have a strong structural correlation to the system being tested. So, of course, developers trying to adopt TDD will follow that advice.

The problem is — and I want you to think carefully about this next statement — a one-to-one correspondence implies extremely tight coupling.

Think of it! If the structure of the tests follows the structure of the production code, then the tests are inextricably coupled to the production code — and they follow the sinister red picture on the left!

FitNesse
It, frankly, took me many years to realize this. If you look at the structure of FitNesse, which we began writing in 2001, you will see a strong one-to-one correspondence between the test classes and the production code classes. Indeed, I used to tout this as an advantage because I could find every unit test by simply putting the word „Test” after the class that was being tested.

And, of course, we experienced some of the problems that you would expect with such a sinister design. We had fragile tests. We had structures made rigid by the tests. We felt the pain of TDD. And, after several years, we started to understand that the cause of that pain was that we were not designing our tests to be decoupled.

If you look at part of FitNesse written after 2008 or so, you’ll see that there is a significant drop in the one-to-one correspondence. The tests and code look more like the green design on the right.

По-моему, ссылку на эту статью я где-то на доу и находил blog.cleancoder.com/…​D-Harms-Architecture.html

Я нигде не упоминал про TDD.
Есть две крайности: либо сложные методы вообще без тестов, либо «открытые» классы без инкапсуляции с хрупкими тестами.

Что делать? Боюсь, буду банален — есть удачные рецепты в шаблонах проектирования.

А я нигде не говорю, что тестов не нужно. Нужны тесты!
Но вот мифическое «покрытие» тестами — не нужно. И тестировать всё подряд — не нужно. Те участки кода, которые не будут допиливаться с огромной вероятностью — тестировать не нужно.

А львиную долю кода, написанную для удобства тестирования — нужно переписать. Просто потому что этот код обладает МАГИЕЙ, им невозможно пользоваться не используя магию. А вот правильный код заклинаниям не подчиняется, и сравнимых результатов вовне не возвращает, делая это только там, где от него это требуется по логике задачи (вынесено в интерфейсы, документацию, API). Но никак не по логике тестирования.

И покрывать тестами стоит не более 20%. Почему? Потому что время — деньги!
Правило Парето никто не отменял.

>И покрывать тестами стоит не более 20%. Почему? Потому что время — деньги!
>Правило Парето никто не отменял.

Фанаты юнит-тестирования тебе возразят аргументом, что по правилу Парето — 80% всех багов продукта, сидит именно в тех 20% кода, что непокрыты тестами. :)

Так какого же достоинства они не покрыты тестами??
И какого достоинства было требовать покрывать остальные 80 — уж не затем ли, чтобы сымитировать реальное тестирование банально нагенеренными через IDE тестами?

А львиную долю кода, написанную для удобства тестирования — нужно переписать. Просто потому что этот код обладает МАГИЕЙ, им невозможно пользоваться не используя магию.

Это что за код «для удобства тестирования»? Arrange/Given? Опять же, если он сложный до магии, это плохой признак. Рабочего кода.

А вот правильный код заклинаниям не подчиняется, и сравнимых результатов вовне не возвращает

Кажется, я вас понял. Сложная логика в void методе. И не разбитая на классы, которые можно протестировать отдельно. Но почему вы считаете, что это правильно?

И на всякий случай повторюсь. Я не говорю о том, сколько нужно покрывать тестами — 20% или 80%. Я говорю о том, что код хорошо бы писать так, чтобы его _можно_ было протестировать. Вот как по-вашему, шаблон фабрика для чего?

Потому что сам факт передачи данных — означает что нужно ПОНИМАТЬ МОЗГАМИ, что должно передаваться, какие именно значения. Для тех кто читает код первый раз на любом из концов связки — это магия!

То ли дело void-метод, который генерит нужные исключения. Или метод, возвращающий просто идентификатор сущности, по которой можно проконтролировать что собственно было сделано — но в реальной логике этот идентификатор используется редко. Зато [если надо] прекрасно даёт пищу статистикам, мониторилкам, и разумеется тестам.

Вы про какую передачу данных? Я про метод, возвращающий значения говорил. В чём тут магия-то?

И давайте c примерами кода, пожалуйста. Иначе слишком абстрактный разговор получается. Повторю вопрос по фабрику. Зачем этот шаблон? Почему нельзя в теле метода просто написать switch?

Подписаться на комментарии