Skip to content

[2024 28 06] Teletext. Testing in Ruby on Rails

linsky-beep edited this page Nov 15, 2024 · 1 revision

Ярославна Поздникина Добро пожаловать на Телетекст! Спикер – Василиса Тюльберова, Ruby backend developer в Evrone с 8-летним опытом. Реагируйте, комментируйте, задавайте вопросы – за самые интересные дарим подарки. https://imgur.com/a/Qe96fcf

Ethielleum вы успешно пинганули 7600+ человек, поздравляю

Василиса Тюльберова Всем привет! Сегодня мы с вами поговорим о тестировании. Что, как, зачем и почему - постараемся разобраться со всеми этими вопросами. Разговор больше расчитан на студентов Хекслета и начинающих разработчиков, но я буду очень рада услышать мнение опытных колег.

Василиса Тюльберова Для начала хотелось бы услышать ваши варианты - зачем вообще писать тесты?

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

Ilya Чтобы ошибки находили не пользователи на продакшне, ибо они почему-то перестают деньги приносить после этого

Andrey Moshkov на это и был рассчет!

Andrey Moshkov нам платят за строки кода

Василиса Тюльберова Индусы в канальчике! 😱

Наталья Мусина Чтобы тестировщики все обратно не возвращали и не ругались)

Василиса Тюльберова Согласна, но это далеко не все плюсы от тестов :)

Василиса Тюльберова Итак, зачем нам нужны тесты:

  • проверить работоспособность кода
  • избежать повторного возникновения исправленных ошибок
  • быстрее дебажить - проще воспроизвести баг в тестах и отлаживать его там же, чем руками
  • тесты позволяют уверенно рефакторить код и постоянно его улучшать
  • тесты позволяют лучше выстраивать архитектуру - сложность в написании тестов зачастую говорит о проблемах с ней
  • тесты позволяют документировать код - зачастую проще изучить незнакомый кусок кода по тестам к нему
  • в целом хорошие тесты дают экономию ресурсов команде разработки, так как ошибки находятся и исправляются на самой первой стадии работы с кодом

Evgeny Sheykin Чтобы убедиться, что фича работает, а в дальнейшем - что она не сломалась из-за изменений в коде

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

Василиса Тюльберова Если вдруг вам что-то будет не понятно из того, о чём я говорю, потому что вы только начали изучать руби и рельсы - не стесняйтесь задавать вопросы, пожалуйста

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

Evgeny Sheykin Это два разных теста: на код ответа и создание сущности? Или можно объединить в один?

Василиса Тюльберова Тут скорее как принято в команде. Я вообще не вижу проблемы в нескольких проверках внутри одного теста. Но существует подход со строгим ограничем "один тест - одна проверка". Я не вижу в нем плюсов, только минусы - увеличиваем количество кода и время прохождения тестов

Василиса Тюльберова Есть мнение, что такие тесты сложнее писать и отлаживать, потому что проверки падают по очереди, но у рспека есть волшебная опция aggregate_failures, которая выдаёт сразу все упавшие и успешно решает эту проблему

Василиса Тюльберова Тесты должно быть легко читать и поддерживать. Старайтесь именовать переменные в соответствие с тем, что вы проверяете - draft_post и published_post всегда лучше, чем post_1 и post_2. Делайте сетап тестов явным и располагайте его максимально близко к тесту, потому что после написания работать мы будем чаще с каким-то одним тестом в файле - очень тяжело собирать shared_context из разных мест и следить за переопределением let(:post) по всему файлу. Не тестируйте приватные методы - так вы усложните себе жизнь при рефакторинге кода. Уменьшайте количество магии в тестах и прописывайте явно весь сетап, относящийся к делу

Andrey Moshkov

Старайтесь именовать переменные в соответствие с тем, что вы проверяете - draft_post и published_post всегда лучше, чем post_1 и post_2.

о, возможно эта строка появилась после ревью моего проекта 🧌

Василиса Тюльберова В процессе работы вы очень часто будете читать код одного какого-то кейса из всех 10-20 в файле. И читаем код в своей работе мы гораздо больше, чем пишем. Поэтому важно, чтобы было удобно и быстро понять, а что происходит в конкретном кейсе

Evgeny Sheykin Как вы относитесь к комментам в тестах? Помогают или только захламляют?

Василиса Тюльберова Вынуждена тебя расстроить - в первом проекте так делают очень многие студенты :) Я как-то даже вебинарчик проводила о best practices в именовании

Василиса Тюльберова Можно на ты, я не против К комментариям в коде я вообще отношусь не очень. Если нужны комментарии - значит, ты пишешь непонятно, подумай ещё раз над именованием, вынеси методы, их названиями объясни, что тут происходит и зачем. Но иногда приходится, обычно это обусловлено сложной бизнес-логикой или категорической нехваткой времени. Так что если есть подозрение, что комментарий упростит понимание кода (в том числе теста) - есть смысл его оставить. Комментировать всё подряд я не вижу смысла

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

Василиса Тюльберова Соблюдайте баланс в тестах. Тесты контроллеров работают дольше, чем юнит тесты, поэтому выносите тестирование сложного функционала и корнер кейсов в юнит тесты, в контроллере же оставьте самый простой вариант на happy flow. E2E тесты нужны, но они медленные, хрупкие и более сложные в написании - оставьте только необходимое количество

Evgeny Sheykin Случайно обычно не падают. Падают специально, после того, как поменялись API). Ну и на UI иногда выбираются не самые стабильные локаторы

Andrey Moshkov у нас ошибка с пробелами в fixtures/users.yml

Василиса Тюльберова Как показывает практика, если прогон тестов занимает больше пяти минут - ими перестают пользоваться локально и гоняют только на CI. Это снижает их эффективность. Опять же, тесты можно распараллелить и гонять в несколько потоков, в том числе локально

Ivan Korolev Иногда бывает. Предположим, что так и случилось - до этого все тесты были зелёными, а теперь один или несколько покраснели. Задача - понять, почему так случилось. Для этого можно посмотреть логи, но там мешанина из логов, относящихся к разным тестам. Как найти в логах записи, соответствующие упавшим тестам? Запустить заново только упавшие тесты нельзя - могут позеленеть.

Василиса Тюльберова Завидую, у меня был момент в проекте, когда в течение недели ни разу на CI тесты не проходили с первого раза. Проект большой, и автор тестов долго не мог добраться и решить проблему :( Жутко бесит и мешает работать. И да, в этом же проекте был эпик на фикс таких тестов, в котором стабильно висело 15-20 задачек, которые по мере сил и возможностей разгребали

Виктор Т. а раскидывать логи нет возможности? хотя бы по директориям

Василиса Тюльберова А можно подробнее? Не совсем поняла, что там может рандомно ломать тесты

Ivan Korolev Это как? Rails льёт всё в один файл log/test.log. И даже если как-то раскидать логи по директориям, то это не сильно поможет. Для одной директории может быть сотни тестов, а логи нужно найти для одного конкретного

Artem У нас прогон тестов локально занимает 6-7 часов 😁

Виктор Т. понял, я просто немного из другого огорода к вам на огонек заглянул, не рубист даже😅

Василиса Тюльберова В смысле, тесты в процессе что-то в лог выкидывают? Тогда к логу нужно добавлять имя файла и название теста, как минимум. Но вообще обычно ты открываешь тест и понимаешь, что дело либо в проблемах со временем/датой, либо с несортированным массивом - это 80% рандомных тестов, мне кажется. rspec позволяет запустить тесты в том же порядке (опция seed), иногда это влияет и помогает отладить

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

camaradaVinogrado т.е. не-happy варианты мы проверяем в классах, которые вызваны контроллерами? Типа того, что с вон теми входными данными класс не создаст запись/не дернет мейлер/вернет failure монаду?

Василиса Тюльберова Как вариант - накидываешь в такой тест дополнительный вывод для логгера, рано или поздно он снова упадёт на CI - и тут ты его легко найдёшь.

Evgeny Sheykin Практикуете "дожим" тестов? Или что мертво должно быть сразу починено?

Василиса Тюльберова Да, это вполне нормальный подход. Желательно проверить, что на failure контроллер выдаст верный код ответа, но если у вас десяток причин для failure - это всё идёт в юнит тесты, а в интеграционных остаётся один кейс

Василиса Тюльберова Не совсем поняла тебя

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

Ivan Korolev rspec позволяет запустить тесты в том же порядке ага, только это может занять много времени

только гонять красный тест в одиночестве в ожидании, пока он снова ляжет этот процесс тоже может затянуться

Тогда к логу нужно добавлять имя файла и название теста Вот этот вариант подходит. Видел кто-нибудь такое, чтобы сразу в config RSpec было для всех тестов?

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

Evgeny Sheykin Скажем, часть тестов упала в прогоне. Будете заново запускать прогон, чтобы проверить, упадет ли снова? И если не упадет, то все ок. Или принципиально важна стабильность тестов, чтобы он с первого раза давал объективную информацию

Василиса Тюльберова before_each

Artem Но юнит тести 'цементируют' tech debt. Если вдруг придется сильно рефакторить, то с большой степенью вероятности все юнит тести пойдут в корзину.

Andrey Moshkov если бы мы сами знали - мы бы уже починили 😄

Василиса Тюльберова Я посмотрю на то, что упало. Если я только что меняла этот код - буду разбираться. Если я туда не лазила - перепрогоню отдельно упавшее. Если прошло - постараюсь решить проблему того, что он случайно падает или создам задачу, если случай непростой или совсем нет времени сейчас

Василиса Тюльберова Расскажешь потом, когда найдёте :) Где-то что-то фэйкером генерите и добавляются лишние пробелы?

Artem Зависимости в фабриках, как раз + сами тести написаны ужасно. Приводит к тому, что в стартовом сетапе создаються по нескольку рекордов каждой сущности в базе.

Andrey Moshkov скорее всего да, из-за поддержки 3х локалей и всего такого

Andrey Moshkov но там оступы ломаются у yml файла в структуре почему-то

Василиса Тюльберова А вот это то, с чем можно работать и делать лучше. Хотя бы не делать хуже с каждым следующим

Василиса Тюльберова А вот это как раз вопрос баланса. Когда пишешь код - обычно примерно представляешь, насколько велик шанс, что ты его будешь менять, и насколько глубоко. И принимаешь решение о соотношении юнит и интеграционных тестов.

Ivan Korolev Когда-нибудь использовали/слышали про TestProf?

Andrey Moshkov оооо, будем холиварить на счет пирамиды тестирования?

Василиса Тюльберова Вопросик - а как часто вы что-то стабите в своих тестах?

Василиса Тюльберова Неть :)

Artem делали профайлинг. Там комплексные проблем. Там и сам по себе перфоманс кода - гавно, и лишнее в фабриках.

Andrey Moshkov на правах что меня тут не могут забанить скину видос про тесты от одной хорошей и знакомой компании https://www.youtube.com/watch?v=o-iY_-Smpks

Artem Жалко. В пользу интеграционных тестов скажу только, что такие тесты позволяют описывать их в терминах бизнеса.

Василиса Тюльберова Ещё одно хорошее правило Пишите тесты на обнаруженные баги. Если баг нашли на тестировании или в проде - это значит, тестов не хватило.

Василиса Тюльберова Просто у меня в проекте сейчас нет пирамиды, поэтому о чём тут спорить :)

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

Andrey Moshkov

Больше тестов - не значит лучше 🙂

а как же 100% покрытие???

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

Evgeny Sheykin если тест ничего не тестирует, то он не входит в 100%))

Artem у нас чувак выкотил ПР со 100% покрытием как то, через полчаса ревертали релиз 😁

Василиса Тюльберова Ну как же, он покрывает строки кода. Есть такой чудный гем shoulda_matchers. Там можно писать expect(User).to have_many(:posts) Но зачем?

Василиса Тюльберова А ты его видел когда-нибудь? :) Мне вот не довелось

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

Andrey Moshkov ответил бы мемом, (Ярославна сказала сразу не кидать мемы с говном, а уже прошло время), но он матный

Василиса Тюльберова Какие инструменты есть в рельсах для тестирования. В первую очередь, это Minitest, он встроен в рельсы и именно им пользуются наши студенты на курсе. В реальной работе чаще встречается другой фреймворк - Rspec. В нём есть возможность использовать вложенные контексты, лениво инициализировать переменные, но за это приходится платить необходимостью изучения по сути отдельного и довольно объёмного DSL. Сложно сказать, что лучше, у всего есть свои фанаты, и тут скорее дело в предпочтениях команды, в которой вы работаете. Любой из них позволяет писать тесты хорошо :)

Andrey Moshkov полюби тесты или тесты упавший прод полюбят тебя

Василиса Тюльберова Судя по опросику - большинство пишет на рспеке. Хотелось бы узнать, что у тех, кто выбрал вариант "Другое"

Василиса Тюльберова Тебя ж не забанят всё равно 😈

Ivan Korolev Как грамотно писать тесты моделей? Есть ли какие-то распространённые практики помимо использования shoulda_matchers?

Писать ли тесты ассоциаций? Писать ли тесты атрибутов? Или только custom методы?

Приемлемо ли писать тесты моделей вида:

    describe User do
        describe "#name" do
        it "cannot be empty"
        ...
        end
    end

    describe "#email" do
         it "has valid email format" do
         ...
         end

     it "is unique" do
         ...
         end
     end
   end

Artem shared_examples (it_behaves_like) - плюсы, минусы, подводные камни? Мне лично, нравится с ними писать тесты, но их минус, что на каждый такой example стартовый сетап и тест прогоняется по новой и это замедляет тесты. Для себя пока нашел выход объединять несколько expect в методы с сематически значащим именем (плюс методы тоже можна параметризировать, как и shared_examples).

Василиса Тюльберова Я перестала такое писать. Зачем, если дальше ты используешь эти ассоциации в коде, и если ты ошибся - упадут другие тесты. Тесты на валидации я тоже не пишу, потому что нет их у меня в моделях :) А если есть - не вижу смысла писать на такие простые вещи, метод validate протестирован в самих рельсах вдоль и поперёк. Если там кастомный сложный валидатор - да, я бы покрыла тестами. Если это простая валидация - проверь один раз в контроллере, что сущность не создаётся с такими параметрами и выдаёт вот такую пачку ошибок, это гораздо важнее с точки зрения бизнеса

Василиса Тюльберова Плюсы - меньше строчек кода Минусы - надо идти искать его определение, когда видишь его посреди файла в каком-то конкретном кейсе. Насчёт сетапа - то же самое будет и без них, если проверять по одному expect за раз. Так что да, собираем проверки в кучу. В общем, я ими пользуюсь, но не часто. У меня они отлично прижились в тестах полиси почему-то

Василиса Тюльберова Я не планировала устраивать разбор Minitest и Rspec с примерами, но если есть какие-то вопросы по ним - с радостью отвечу

Василиса Тюльберова Самая холиварная тема - что лучше использовать для работы с данными в тестах, фикстуры или фабрики. У любого из этих подходов есть свои недостатки. Фикстуры позволяют заполнить БД предопределённым набором данных, который вы описываете в yaml. Фабрики предлагают другой подход - сетап данных перед каждым тестом (или их набором).

Василиса Тюльберова Расскажите, если вам было больно в проекте с тем или иным подходом. Или может быть надо подробнее рассказать и показать, о чём это я вообще

Василиса Тюльберова Судя по нашему опросику, большинство использует фабрики. Но есть несколько человек, у кого и то, и другое в проекте. Расскажите - как это работает?

camaradaVinogrado а есть же VCR с опцией периодического обращения (обновления кассет) для случаев, когда можно обратиться к внешнему sandbox API (или безопасно к prod API)

Jackson Подробнее пж про недостатки и преимущества пж

Василиса Тюльберова Счас всё будет :)

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

Andrey Moshkov землю — крестьянам, фабрики — рубистам, фикстуры - хекслету!

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

Andrey Moshkov холивар уже был что моки это не стабы?

Artem чтоб холиварить на эту тему, нужно знать разницу 😁

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

Василиса Тюльберова Расскажи нам уже просто и понятно, чем они отличаются 🙏

Andrey Moshkov так был бы я умным, я бы рассказал непременно, а так я жду рассказ от вас

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

Василиса Тюльберова В случае с фабриками нужно создавать все нужные сущности перед тестом, это требует времени, зато даёт ясность того, что же вы сейчас тестируете. Сущности проходят валидации в модели, это снижает риск странных ошибок в тестах. Кроме того, вы уверены, что в БД нет никаких других записей, кроме тех, что вы только что создали. Ну и легко можно тестировать стейт без записей

Василиса Тюльберова Я знаю тех, кто использовал фабрики, отчаялся и перешёл на фикстуры. Знаю обратные примеры. Так что здесь нет серебряной пули, к сожалению. Скорее, придётся смириться с недостаткими выбранного вами способа

Ivan Korolev А нам это важно? Или это как-то должно влиять на прохождение теста?

Andrey Moshkov

Кроме того, фикстуры не валидируются, поэтому всегда есть шанс получить красные тесты с непонятными ошибками из-за невалидных данных

после того как упоролись в невменько ошибки пару раз - сделали валидацию фикстур на CI

Evgeny Sheykin Я так понял, что при выборе варианта с фабриками меньше проблем с зависимостями данных и лучшая поддерживаемость? А какие минусы, сложность начальной настройки и скорость тестов?

Sergey M.

Я знаю тех, кто использовал фабрики, отчаялся и перешёл на фикстуры так Мокевнин же)

Sergey M. а блин, частицы не нету)

Artem как раз с фабриками могут быть проблемы с зависимостями на больших проектах со сложной бизнес логикой

Ivan Korolev Когда-то читал эту статью. Довольно понятно рассказано. Можно даже не читать и посмотреть только на картинки https://habr.com/ru/articles/577424/

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

Василиса Тюльберова Может всё-таки фабрики, а? 😈

Sergey M. Василиса, а по фотографии лечите?) https://imgur.com/a/RKZ8BxI

Andrey Moshkov Василиса, это шарлотан, не слушай его

Василиса Тюльберова Ну вот я про него в первую очередь :)

Andrey Moshkov от от дедушки ушел, он от Хекслета ушел и от твоих советов уйдет

Василиса Тюльберова Учитывая, что по основному образованию я врач - в основном этим и занимаюсь 😆

Andrey Moshkov значит ты сможешь констатировать смерть руби?

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

Василиса Тюльберова Надеюсь, я умру раньше :)

Василиса Тюльберова А чего тут лечить? :)

Andrey Moshkov фикстуры тебе еще данные для девокружения наваливают

Andrey Moshkov а с фабриками придется или еще и фикстуры добавочно делать, или сиды, так?

Andrey Moshkov а тут одним выстрелом двух зайцев

PaulReedSmith Ничеси 100 сообщений в руби, а не в рандоме )

Andrey Moshkov как говорится - делай проще, ты чо, не ленивый?

Ivan Korolev Тогда в сетапе теста явно удалить все записи и только потом создавать нужные и выполнять проверки

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

Clone this wiki locally