Спасибо за работу. Спустя 7 лет наконец-то снова появилось ощущение естественности происходящего при использовании почтового клиента. Отдельное спасибо за быстрый доступ к новым письмам из панели статуса, очень удобно.
Кучу лет в openvpn-клиенте была бага не позволяющая вводить пин от рутокена если openvpn собран с поддержкой systemd (а он всегда собран с поддержкой systemd в бинарных дистрибутивах). В генту все исправляется одним USE-флагом, в остальных дистрибутивах нужно было собирать пакет самостоятельно.
Хах, хорошие коммиты кучу раз меня спасали. Любой код когда-нибудь может стать legacy-кодом (достаточно не трогать его около двух лет). И тогда только git blame сможет объяснить что имел ввиду разработчик.
Может я конечно ошибаюсь, но я сильно сомневаюсь что ваш выбор развития в IT-области был обусловлен именно деньгами, скорее всего вам это направление нравилось еще со школы, а высокие зарплаты были просто приятным бонусом. Как-то не укладываются у меня в голове мысли типа: "программистам платят мало, пойду-ка я лучше в нефтянники".
То есть элита — эта те, кто имеют возможность развиваться и при этом развиваются?
Никого не осуждаю, но все же стоит признать, что программистам просто повезло что они востребованы, никакой вселенской справедливости во всем этом нет.
Окей, we need to go deeper.
Не нужно ли при написании «истинного» модульного теста еще мокнуть реализацию языка программирования, ведь от нее тоже много чего зависит? И если мы этого не делаем, то наш модульный тест по факту опять интеграционный.
И даже если мы выполним эту задачу (хоть это и невозможно), можно пойти еще глубже и начать думать что для «истинного» модульного теста нам еще нужно мокнуть «железо» на котором этот тест запущен.
И продолжаю называть. Потому как неспроста никто однозначно не говорит что же такое модуль в модульном тестировании. Вы правы что модуль это часть приложения которую можно протестировать независимо, но нужно еще определить независимо от чего именно его нужно тестировать.
К примеру, нужно ли тестировать систему независимо от работы стандартной библиотеки языка.
Просто вы для себя в качестве модулей выбрали так называемые «компоненты» (и я тоже). Что при этом мокать это всегда интересный вопрос, как правило при принятии решения учитываются время прогона теста, сложность подготовки окружения для зависимости, наличие модульных тестов для зависимости.
Я читал вашу статью и хотя в целом я поддерживаю разделение элементов системы на модули и компоненты, концептуально это не играет роли, сотрите из своей схемы модули и компоненты превратятся в те же модули. К тому же, ваша схема неверная, у вас связи от модуля идут только к одному компоненту, в реальной жизни это не так. К примеру вызов библиотечной функции можно считать обращением к модулю и вряд ли вы захотите использовать библиотеку не покрытую тестами.
Я это к тому, что если считать компоненты независимыми черными ящиками со строгим контрактом, то ваши компонентные тесты вполне можно считать модульными, а если знать о наличии модулей, то эти тесты по определению интеграционные/функциональные.
Я конечно догадался что вы про интеграционные тесты. Просто на низком уровне интеграционное и модульное тестирование не так уж и сильно отличается. Если я мокаю в тесте одну из зависимостей с сохранением интерфейса и проверкой передаваемых в нее параметров, это все еще модульный тест или уже интеграционный?
Признаю, некорректно выразился. Под тестами я конечно имел ввиду тесты которые написаны нормально, как правило это те тесты где очевидны условия, действия и результат. От таких тестов как в описанной ситуации конечно больше вреда чем пользы, особенно если они не только не находят баги, но и начинают «мигать».
При этом стоит заметить, что описанный «юнит»-тест уже не совсем подходит под определение, ведь на самом деле он тестирует 2 функции в интеграции, ну и соответственно начинает иметь признаки интеграционного со всеми вытекающими плюсами и минусами.
Данную оценку вполне можно дать при достаточности исходных данных… Но их как правило достаточно не бывает.
Хотя вру, бывает — когда заранее известно что разрабатываемая система временная и будет выкинута или не будет никак дорабатываться. К примеру, системы-костыли необходимые только на переходный период изменения какого-то бизнес-процесса, мосты между legacy-системами, всякие хакатоны и т.п.
Прошу заметить, что я не утверждаю что в этих системах не нужны тесты, я говорю о том, что «стоимость» написания или ненаписания тестов тут можно как-то оценить.
Если же система предполагает развитие, во что она потом превратится и нужны ли ей будут тесты большой вопрос, может быть бюджет на систему закончится еще до ее внедрения…
Ну и последнее, написание юнит-тестов не всегда замедляет решение задачи, если задача это не какая-то элементарная вещь, то новые юнит-тесты начинают работать еще до завершения текущей задачи и тем самым ускоряет ее решение, хотя тут вы вероятно мне не поверите.
Оглядываясь назад в те времена когда я не писал тесты, мне иногда кажется что я был очень смелым.
Но на самом деле тут должна быть картинка «Слабоумие и отвага» с Чипом и Дейлом.
Сегодня выполняя задачу без тестов я чувствую себя лгуном, так как говорю что задача сделана, а гарантий этого я не даю даже самому себе.
Основные плюсы юнит-тестов вы так и не озвучили.
Дело в том что в отличии от других уровней контроля качества, юнит-тесты лежат максимально близко к коду. Если человек пишет юнит-тесты, то это непременно влияет на процесс разработки.
Вы говорите насколько написание юнит-тестов увеличивает затраты на задачи,
но не говорите насколько они уменьшаются при наличии ранее написанных тестов.
Можно вспомнить хотя бы про то, что наличие юнит-тестов позволяет не бояться вносить изменения в код, а так же про то что юнит-тесты заставляет программиста писать код лучше (кривой код очень сложно покрывать юнит-тестами).
А еще юнит-тесты можно применять в качестве документации к модулям системы, у новых участников команды не будет возникать вопросов как использовать тот или иной компонент.
А еще… Да много еще чего.
Короче говоря, юнит-тесты это больше про процесс разработки, а не про контроль качества.
Коллизии возможны, тест при этом падает сообщая что найдено более одного элемента. В этом случае прибегаем к помощи css/xpath (обычно все же css), но используем их для обозначения области поиска, выше приводил пример:
within('#comments') do
click_button 'Ещё'
end
Локатор в итоге указывает на контейнер, а не на сам элемент, соответственно меньше вероятность что он изменится из-за правок верстки.
В нашем проекте, эта практика работает почти всегда.
Иногда конечно бывают исключения, но они часто говорят о проблемах юзабилити.
К примеру, тест находит 2 кнопки с текстом "Ещё", это значит что на странице действительно находится 2 такие кнопки и какую из них заметил будет нажимать пользователь — большой вопрос.
В случаях когда наличие нескольких элементов действительно оправдано (например, идентичный пагинатор сверху и снизу страницы), можно использовать уточнения:
within('.article_list') do
click_link('следующая страница', match: :first)
end
Я бы сказал, что использовать эту штуку можно, если по факту проверяется состояние одной сущности с помощью нескольких проверок, а написание для этого случая отдельного матчера нецелесообразно или ухудшит чтение теста.
Спасибо, меня смутили ваши фразы «Если есть ошибка, то тест должен упасть» и «не совсем ошибка, но хорошо бы знать». Кроме того, одна из нагугленных страниц говорила о том, что при провале soft assert'а тест все равно будет помечен как пройденный, потому решил что мы говорим о разных вещах.
Не не не, aggregate_failures в rspec это не soft assert, тест падает, просто падает не на первой проверке, а после проведения всех проверок.
Мы используем его когда точно уверены что каждую из этих проверок нет смысла проводить в изоляции.
Например проверки наличия всех необходимых элементов на странице (нет смысла второй раз готовить окружение, чтобы проверить что помимо блока «новости» на странице присутствует блок «комментарии»).
Бить никого и не собирался, и вероятно это действительно наша специфика, но у нас в проекте нет обилия кнопок, ссылок без текста (если их много пользователям сложно разобраться что делает каждая из них).
Да, иногда приходится писать хелперы типа click_burger с css-селекторами, или использовать xpath чтобы найти конкретный элемент среди множество, но как и писал выше, это скорее исключение.
А в случае с формой опросником я бы проверял что после нажатия на кнопку появился новый вопрос, или завершился тест, а не то что появилась новая кнопка вместо старой.
По поводу нескольких проверок в одном тесте.
В рубишном rspec'e на этот случай есть фича aggregate_failures. При включении, в случае провала одной из проверок, тест продолжает выполнение, а потом выдает все собранные ошибки. В UI-тестах используем очень часто, в остальных случаях пригождается значительно реже.
describe 'регистрация' do
before do
fill_in 'Имя пользователя', 'vasya_pupkin'
fill_in 'Пароль', 'qwerty'
fill_in 'Подтверждение', 'qwerty'
click_button 'Регистрация'
end
it 'работает' do
expect(page).to have_text('Вы зарегистрированы!')
end
end
Такой тест значительно проще пишется и читается. css и xpath все еще бывают нужны, но это скорее исключение из правил и обычно используются в качестве ограничения зоны видимости:
within('#comments') do
click_button 'Ещё'
end
Поиск таких элементов идет дольше, но для нас на текущий момент более важны читаемость и поддерживаемость тестов.
Конечно, у нас даже проблему таяния ледников не могут решить, а все что касается климата сводится к идиотским акциям типа «час Земли», «на работу на велосипеде» и т.п. Вместо этого люди всерьез занимаются совершенно неактуальными вопросами, которые будут требовать колоссальных русурсов, которые однозначно неоткуда будет получить без еще более катастрофических последствий для экологии.
Проявиться она могла в абсолютно любой момент, чуть более «тяжелая» транзакция, чуть более «шустрое» железо с sidekiq'ом и бага проявляется снова, только уже не постоянно, а один раз на миллион. И найти концы в этом случае было бы значительно сложнее. Вобщем, ИМХО, улучшение производительности вследствие банального рефакторинга вам только на руку сыграло.
Loriowar ну серьезно, неужели ты всерьез считаешь, что упомянутая проблема с sidekiq'ом появилась из-за того, что кто-то улучшил быстродействие? Так вы хотя бы о проблеме узнали.
По теме, рефакторинг это не заболевание, а постоянный и крайне необходимый процесс. А задача разработчика обосновать эту деятельность (независимо от того, в рамках отдельной задачи ведется работа или нет).
Понимаю что статья для новичков и вообще является перводом, но тут явно не хватает описания минусов всех описанных методов помимо аутентификации по сессии (а они есть и еще огого какие). Кроме того можно было бы хотя бы перечислить некоторые менее очевидные методы (тот же kerberos, к примеру).
Статья пример неправильного применения коротких функций. Каша из слабосвязанных аргументов, которые хоть и иллюстрируют проблему, но совсем не ту о которой пишет автор.
По факту, мелкие функции действительно бывают огромной проблемой, но только потому, что выделяют не те фрагменты и не в те места.
Действительно все крутится вокруг SRP. И хоть в ООП данный подход и приводит к появлению множества различных уровней абстракций, при правильной реализации «осознать» такой код значительно проще, чем кашу из методов (будь они хоть длинными, хоть короткими).
У нас эта проблема на ревью решается=) Постоянно джунов прошу разбивать свои функции и давать им адекватные имена. В данном случае только строгий контроль может чему-то научить. И да, в случае джунов, имхо, пусть лучше мелкие функции пишут, багов в этом случае по статистике меньше.