В рамках подготовки Joker 2016 вышел пост про легаси, который вызвал бурное обсуждение тестирования в Java, которое мы решили продолжить в интервью с Николаем Алименковым.
Николай — специалист в области разработки на Java уже с 12-летним стажем. Помимо основной рабочей деятельности, он — сооснователь и тренер тренингового центра XP Injection, активный участник и докладчик на международных конференциях. При его участии были организованы IT-конференции Selenium Camp, JEEConf, XP Days Ukraine и IT Brunch. Мы поговорили как о том, что можно улучшить в области тестирования в своей команде «здесь и сейчас», так и о том, к каким технологическим переменам нам следует готовиться в будущем.
— Николай, мой первый вопрос — про самотестируемый код, использующий ассерты внутри самого себя. Твоё отношение к этой практике.
— Мне кажется, что эта идея заведомо была некорректной. Предполагалось, что такие проверки могут заменить тесты и сделать код самоверифицированным, но, к сожалению, не срослось. Причина очень простая: в этой идее полагаются на то, что, разработчик, когда он пишет код, одновременно думает и о реализации, и о побочных явлениях, которые могут произойти. Но разработчики не так хорошо переключают контекст налету. Вместо этого юнит-тесты заставляют делать сначала фокус на тестирование, а потом возвращаться к фокусу на разработку.
Ассертами заменяют больше не тесты, а рантаймовые проверки: например, если что-то равно null, то нужен Exception. Но ассерты же не являются обязательным к выполнению, можно их отключить, и тогда проверка не выполняется. Сейчас есть много других подходов, которые позволяют лучше сделать. Например, подход с аннотациями, где мы можем на входные параметры метода или на переменную поставить аннотацию NotNull. И эту аннотацию можно ставить в обработчики, которые будут проверять и бросать Exception. Сейчас имеются специальные validation-фреймворки, которые работают достаточно неплохо.
Но ассерты, мне кажется, умерли, как только появились. Я видел очень много кода в очень многих компаниях, и я не видел ни одной компании, в которой их использовали серьезно. Вот прямо вот ни одной.
— В теории все понимают, что test-driven development — это здорово и хорошо. Но на практике не весь код оказывается перекрыт модульными тестами. По-твоему, кроме лени разработчика, почему так бывает?
— А тут дело не в лени разработчика. Тут дело в двух причинах, на мой взгляд.
Первая — это то, что люди не умеют этого делать. Для того, чтобы разрабатывать по TDD, необходима подготовка. И мало этого, необходимо понимание инструментария, как им пользоваться и какое он дает преимущество. Человек, который проходит курсы, или сам изучает TDD, или садится работать с кем-то грамотным, кто уже работает по TDD, видит столько преимуществ в работе, что после этого ему становится понятно, что глупо так не делать.
А вторая причина — это то, что многие из разработчиков, особенно с завышенной самооценкой, «включают режим архитектора». Это когда человек посмотрел на задачку одним глазком и говорит: «Окей, все, я вижу. Вот здесь у меня будет фабрика, здесь будет такой-то паттерн, здесь — такой-то». И он сразу эти мысли выплескивает в код. Потом наступает момент, когда надо интегрировать всё то, что он «напроектировал», с остальным кодом. И становится ясно, что оно не интегрируется. Или же кто-то смотрит код на code review, и становится видно, что все методы гигантские, ничего не понятно, пятой вложенности if-ы. Наверняка все видели примеры, когда «Hello, world!» при помощи дизайн-паттернов можно изобразить так, что не разберешься, что перед тобой «Hello, world!».
Когда ты работаешь по TDD, то ты написал тест, и теперь твоя задача просто в том, чтобы он заработал. Твоя задача не сделать суперклассный дизайн. Задача сделать суперклассный дизайн возникает уже после того, как код заработал. Ты потом смотришь на него и говоришь: «Вот я простое решение написал. А можно его как-то сделать красивее? Можно сделать его как-то более элегантным? Или reusable?» И если нет — ну окей. Оно работает и работает, поехали дальше. То есть вот причины: «режим архитектора» и неумение писать тесты, и неумение работать с правильным инструментарием.
— Ну, все-таки, мне кажется, тут дело не только в неумении писать тесты, но еще и в неумении писать тестируемый код. В сложностях, связанных с тем, чтобы написать тестируемый код.
— А его и не надо писать. Если ты работаешь по TDD, то перед тобой не стоит задача писать тестируемый код, потому что у тебя нет шансов написать его нетестируемым. Вот же в чем прикол!
— Гм! Да уж! Простая идея, ничего не скажешь!
— Когда ты изначально в тесте нарисовал, как должен выглядеть код, чтобы тесту было удобно, то тогда у тебя заведомо получится тестируемый код, который красиво можно протестировать, который хорошо интегрируется, в котором классный API. А вот если ты постфактум решил уже реализовывать тест, то, конечно, тестируемость играет очень большую роль.
Если ты сначала написал какой-то код, а потом подходишь к нему и говоришь: «Ну, сейчас я свой первый юнит-тест на нем и напишу» — то часто возникает «затык». Ну вот классический пример — когда ты сделал три boolean-параметра в методе. И вот ты передаешь их: foo(true, false, true). А потом сам же видишь это и говоришь: «Ааа, что это такое вообще? Ничего не могу понять!» Или, например, для того чтобы вызвать один метод, тебе надо столько сетапа сделать, что ты уже забываешь, зачем ты вообще этот тест пишешь. Это ровно то, что происходит, когда ты пишешь постфактум тест.
А если ты работаешь по TDD, то происходит так: пишешь-пишешь ты тест, наконец смотришь: «О, вот красивый API получился! Именно так и должно выглядеть, так оно и надо!» И сгенерировал быстренько весь API. Потому что благо — если мы говорим про Java-разработку — очень много всего генерируется с помощью IDE из теста и не надо писать руками. В итоге тот человек, который работает по TDD, работает чуть быстрее за счет этого фактора. Он не пишет руками ни одной сигнатуры метода, ни одного конструктора, ни одного поля. Это все генерируется. Причем мегабыстро. Все, что пишет человек, который работает по TDD — это реализации методов. IDE берет на себя создание классов, конструкторов, геттеры, сеттеры, декларации методов, очень сильно в этом помогает и экономит огромное количество времени.
— Но все-таки тесты иногда заставляют вмешаться в реализацию кода. Вот, например, хочется сделать private какой-нибудь, а приходится его открывать.
— Нет, не приходится открывать. Тут мы возвращаемся к тому, что не умеем правильно делать. Потому что когда ты хочешь протестировать что-то внутри, и оно закрыто у тебя в private — это обозначает всего лишь то, что твой класс, который ты тестируешь, стал обладать слишком многими responsibility. И это значит, что по-хорошему ты должен поменять несколько свой дизайн и вынести аспект кода, который ты хочешь протестировать, в отдельный класс, задача которого будет делать именно это. Опытному разработчику это подсказка, что его дизайн стал слишком комплексным. Можно, конечно, сделать один класс, в который напихать вообще все. И он будет уметь и печатать, и сохранять базу, и трансформировать в JSON, и высчитывать какие-то алгоритмы. И в итоге получится over-complicated solution. А когда работа идет в команде, то такой код очень тяжело будет модифицировать, потому что все по любому чиху будут лезть в этот код и менять его. И вот это подсказка. Если ты посмотрел и говоришь: «А как я это протестирую? Мне надо это открывать» — все, это сразу звоночек о том, что надо менять дизайн.
— Как интересно. Окей. Ну тогда поговорим по поводу такой вещи, как необходимость в тесте использовать базу данных. Из-за необходимости постоянно иметь доступную базу данных, конфигурировать подключения, запуск модульных тестов, зависимых от базы данных, у нас превращается в ад. Мы их отключаем в сборочных скриптах, честно говоря. Что делать? Где-то мы пытались писать моки источников данных JDBC — очень сложно.
— Я, на самом деле, удивлен, что уже прошло столько времени, а до сих пор такой вопрос возникает. Это значит, что я не везде, где это мог донести, донес.
Я давно начал об этой проблеме говорить, и у меня есть доклад под названием «TDD for database related code, how is it possible?», который я рассказывал на некоторых больших конференциях. Вот здесь и здесь есть видео, а здесь — презентация, где я в режиме лайф-кодинга демонстрирую, каким образом это делать, каким образом подключается база данных, каким образом эта база данных потом используется в тестах.
Во-первых, не надо мокать API JDBC. Потому что если мы будем делать моки на JDBC API, что мы будем тестировать? Мы будем тестировать не то, что наша интеграция с данными правильно работает, а что мы послали вроде как более-менее верно сформулированный запрос SQL. Но в SQL можно легко перестроить запрос так, что он будет внешне другой, а по сути тот же самый. Например, переставить части AND-а между собой. Можно сказать: WHERE «user» = ‘Вася’ AND «role» = ‘admin’, а можно наоборот. Получается, что если мы будем делать моки на такие запросы, то при таком изменении, которое функционально ничего не поменяло, мы ничего не протестируем. Поэтому считается, что на базу данных надо писать именно интеграционные тесты, которые будут поднимать реальный контекст, поднимать реальную базу и на этом работать.
— Но работать с настоящей базой — это очень медленно!
— Тут на помощь приходят in-memory базы данных. Есть старый добрый HSQLDB, то есть H2, который является, скажем так, его современной версией. Мало того, что H2 позволяет подниматься как in-memory-база, он ещё может работать в режимах синтаксиса поддержки разных баз данных. То есть можно сказать: «H2, поднимись и работай в синтаксисе MySQL. H2, поднимись и работай в синтаксисе Oracle». Они не на 100% совместимы но, тем не менее, большую часть проблем решают.
Плюс к этому написана уже масса статей, как быстро — именно быстро! — поднять базу с помощью RAM-диска. То есть мапить ее не на дисковую подсистему, а делать RAM-диск и мапить ее в оперативную память. Ведь для того, чтобы писать юнит-тесты, нам нет необходимости поднимать дамп на 10 гигабайт из продакшна, правильно? Мы же не собираемся гонять какие-то специфические перформанс-сценарии. Мы пишем обычные юнит-тесты, которые должны проверить нашу логику работы на небольшом количестве записей.
И, наконец, ещё один помощник в этом — это DbUnit. DbUnit, который позволяет очень легко манипулировать наборами тестовых данных, делать их очень удобно, делать их в XML, в JSON, в чем удобно. Но лучше всего, на мой взгляд, работать для этих задач в XML, потому что так мы получаем структурированные данные. И в этом случае мы просто легко можем иметь дата-сеты, сфокусированные на конкретные тесты. То есть, например, если нам необходимо проверить, что поиск работает, мы вставляем пять-десять записей, которые демонстрируют все разнообразие того, что мы ищем, и мы сфокусированы только на этих 10 записях. Причем мы вставляем только те колонки, которые нам необходимы.
Если мы говорим о ситуации, когда есть данные, которые связаны между собой, то здесь работают такие трюки как, например, отключение на лету констрейнтов. То есть мы, например, открываем Connection и говорим: «Отключите, пожалуйста, проверку всех констрейнтов». Тут зависит от базы данных: где-то это можно сделать глобально только, где-то это можно сделать в рамках коннекшена. Мы отключаем констрейнты и это позволяет нам, если мы ищем, например, юзеров, не отвлекаться на дополнительные данные, которые обязаны быть с юзерами, и не вставлять их.
Еще один трюк — это возможность делать тест в транзакции. Это значит, что в тесте в транзакции данные вставляются, в этой же транзакции делается тот запрос, который необходим для того, чтобы эти данные получить, и в конце мы откатываем. Понятное дело, что не все тесты могут быть написаны с таким подходом. Особенно хорошо это работает для тестов, которые получают данные. Для тестов, которые вставляют данные, это не всегда хорошо. Потому что как раз там и интересно посмотреть, как сработали констрейнты, бросился ли правильно exception, правильно ли мы его перехватили, обернули и так далее.
То, что я перечислил — не единственное решение. Это — то, что есть, то, что известно всем, то, что уже известно давно. Делают все новые и новые решения, которые позволяют эту функциональность расширить, сделать более удобной и сделать тестирование баз данных комфортным.
— Довольно давно у меня сложилось впечатление, что автоматизация тестирования пользовательского интерфейса — это настолько сложная и ненадёжная вещь, что лучше доверять её людям, работающим по более или менее расплывчато сформулированному тестовому сценарию, чем писать автоматические скрипты, и я для себя на автоматизации тестирования UI поставил крест.
— Ну это ты зря, потому что в последние годы тулов и подходов появилось огромное количество. Если говорить про web-интерфейс, то, действительно, когда-то давно мы ходили в обход браузера, выдумывали разные хаки, как можно через JavaScript что-то «дернуть» на самой странице. Но сейчас все стало гораздо проще, потому что появляются определенные стандарты. Если мы говорим про инструментарий для web-приложения, то это WebDriver, поддержка которого теперь уже расходится по самим браузерам, и уже сами браузеры внутрь вставляют реализацию WebDriver-а, которая позволяет контролировать браузер удаленно. И получается, мы из тестов полноценно управляем браузером, делаем все, что угодно с ним так же, как это делает обычный пользователь. Мы можем получать любую информацию из браузера, мы можем вытаскивать все логи, внутренние обработки, коммуникации с внешней средой.
Точно так же очень сильно расширяется тестирование мобильных приложений. Появляется все больше и больше вариантов работы, как с реальными устройствами, так и с эмуляторами, где тоже есть свои фреймворки, на которых достаточно легко писать через API-тесты.
По поводу реальных устройств — одно из направлений, которое сейчас активно развивается, это роботизированное тестирование. Когда печатаются на 3D-принтерах мини-роботы, в которых вставляют телефон, и они программируемы. То есть имеется микроконтроллер, этот микроконтроллер можно программировать, посылая ему команды. Соответственно, у робота есть перо с резиночкой на конце, вроде механического пальца. Устройство ставится в определенное положение, и дальше все это легко работает.
— Как он считывает при этом то, что на экране?
— То, что на экране, считывается благодаря подключению телефона. Ты можешь считывать то, что на экране, и в зависимости от этого делать какие-то действия. Понятно, что оно не подойдет для всего, но работу с какими-то приложениями очень легко автоматизировать. Этот подход сейчас только активно развивается. Нет ещё production-решения, которое бы могло заменить тестировщиков в этой области. Но, тем не менее, это становится все более и более популярным.
— Фантастика! Но нашей команде до этого, конечно, ещё далеко.
— Пожалуйста: сейчас продолжают развиваться краудсорсинги по тестированию, когда вы можете отправить свое приложение в большую платформу, на которой работает куча специалистов — китайцы, индусы, наши соотечественники — которые получают рейты за часы, проведенные за работой. Вы можете масштабировать свое тестирование как угодно в полуавтоматическом, назовем это так, режиме.
— Эти люди получают тестовые сценарии?
— Они получают тестовые сценарии, они получают приложение с описанием, как оно должно работать, и проводят тестирование. Это — интересная возможность, о которой многие не знают и мучаются, не имея команды тестировщиков на плаву, хотя могли бы отдавать это туда.
Плюс к этому на сегодня еще одна очень активно развивающаяся область — это визуальное тестирование (например, при помощи Applitools). Если объяснить очень простыми словами, то выполняется некий сценарий под приложение с помощью WebDriver, если мы про web говорим, и снимаются скриншоты. И дальше реализованы алгоритмы сравнения скриншотов с целью определения изменений. Вот был, например, вчера сделан скриншот, и мы посмотрели на него и сказали: «С ним все круто. Это будет наш baseline». А теперь мы сегодня сняли скриншот. Есть алгоритмы анализа этих скриншотов, которые позволяют отследить, где именно какие изменения произошли, и сгруппировать их.
— И, допустим, мы поменяли шрифт, картинка изменилась. И что? тест теперь не прошел?
— Нет! Они умеют отрабатывать такое. Они умеют группировать эти вещи. То есть они говорят: «Ага, здесь мы видим, что изменился шрифт». Просмотрели еще несколько скриншотов и говорят: «Это тип изменения №1 — поменялся шрифт». И ты должен его подтвердить. Ты должен сказать: «Это правильно, это мы действительно поменяли в приложении шрифт». И, кроме того, ты можешь в настройках сказать, что если еще раз произойдет изменение шрифта, то просто не уведомлять об этом.
Это очень интересно, потому что это дает нам возможности тестировать еще более широко. Вот смотри, например, у нас есть какой-то тестовый сценарий, и мы хотим его выполнить, чтобы проверить, что все теперь хорошо. А что такое «все теперь хорошо»? Раньше мы должны были бы явным образом указать: «В этом поле — такие-то данные, тут в подписи показалось вот это, тут выскочило нотификационное окошко», и так далее. А визуальное тестирование вместо этого позволяет просто показать, как должен выглядеть скрин в случае, если все хорошо. И тогда если ты случайно забыл про какую-то проверку, ну, например, про одно из полей, которое тоже надо было проверять, то визуальное тестирование позволит это сделать автоматически.
Если у тебя изменилась форма, оно тебе скажет: «Ого, а вот тут новое поле появилось». Ты говоришь: «Confirmed, появилось, все правильно». И у тебя новый baseline создается. И это большое направление сейчас, очень популярное.
И вот еще один из примеров, как может быть использовано визуальное тестирование: когда необходимо протестировать визуально сайт на разных разрешениях экрана. Представь, какое это количество ручной работы. Есть четыре основных браузера: Firefox, Chrome, IE, Safari, помножь это на количество разрешений. Это практически нереально делать руками. А инструменты визуального тестирования позволяют тебе указать разрешение экрана, затем получить baseline-скриншот для заданного разрешения и так далее.
В общем, в тестировании пользовательских интерфейсов сейчас все хорошо.
— Звучит потрясающе.
— И это реально работает, что интересно. Одна из наших конференций, Selenium Camp, целиком посвящена именно современным технологиям в автоматизации тестирования. Сейчас продукт Selenium больше известен как WebDriver. Но когда мы начинали, то еще WebDriver-ом это не называлось, это называлось «Селениумом». И мы тогда стали первой конференцией в мире, которая была посвящена целиком и полностью этому продукту.
Если кто хочет по этой теме глубже посмотреть, то у нас есть огромное количество видео. В открытом доступе абсолютно за все годы. Можно пойти и посмотреть, как это делают в известных компаниях наподобие Google, Facebook и прочих.
— Какие сейчас, на твой взгляд, наиболее активные точки роста у методов тестирования? В какую сторону будет в ближайшее время развиваться вообще эта дисциплина — тестирование?
— Ну, понятное дело, что глобально развитие направлено на автоматизацию.
— Автоматизацию тестирования UI?
— Не обязательно! Мы ведем речь еще и об автоматизации, например, тестирования API.
Одна из проблем, которая стоит перед разработчиками, это если есть большое количество бизнес-сценариев, то должен кто-то быть, кто руками должен будет создавать эти сценарии и «учить» автоматизированные тесты выполнять эти сценарии. То есть все равно должен присутствовать человек, на котором висит ответственность, чтобы он не пропустил каких-то сценариев, чтобы он перебрал все комбинации, чтобы ничего не было недопокрыто. И это плохо. Потому что мы опять остаемся с человеческой составляющей.
А сейчас ведется работа по достаточно интересным направлениям — автоматическим генерациям тестовых сценариев на основании бизнес-моделей и workflow самого приложения. Идея очень проста. Если мы представим себе работу приложения неким workflow-графом с переходом состояний, и на каждом из переходов состояний мы будем указывать, какого типа данные необходимо предоставить в приложение, чтобы этот переход осуществился, то дальше, если мы все такие переходы опишем, то мы получим граф нашего приложения. То есть мы поймем, как можно по нему ходить и в какие точки мы можем доходить, какие точки являются финальными, откуда мы уже никуда не можем выбраться, и прочее.
— Это лишь до некоторой степени точности может описать приложение? Естественно, не все его аспекты работы?
— Ну почему? Как раз все аспекты можно описать. Потому что любой бизнес-сценарий должен быть выражен в этом flow. Если он не выражен в этом flow, то это значит, что он не покрыт. Это очень натурально, потому что эти flow являются представлением клиента, представлением заказчика, представлением конечного пользователя о вашем продукте. Я о любом продукте думаю в разрезе flow, что я могу сделать, какие шаги я могу пройти, каких результатов я могу добиться. И это красиво визуализируется в виде графа. И после этого на этот граф натравливается своеобразный робот, который генерирует все возможные сценарии, потому что у него есть направление, как можно по этому графу ходить. Где-то он заходит в повторные веточки, там можно указывать глубину и так далее. И он автоматически генерирует все возможные сценарии. И дальше все, что нужно сделать, это просто автоматизировать каким-нибудь своим инструментом, который вы используете для тестирования, сами переходы. Ну, то есть нужно сказать, например, что под логином подразумевается либо вызов API логина, либо что я кликну на клавишу «Логин». И тогда получается, что работа автоматизаторов будет заключаться в автоматизации конкретных шагов, а уже сами сценарии будут появляться на базе прохода вот по такому графу. Это очень интересное направление, потому что оно позволит избежать многих проблем с поддержкой тестовых сценариев. Когда, например, у вас написана тысяча сценариев, и меняется какой-нибудь промежуточный шаг, то надо пойти во все тысячу сценариев и учесть его.
Плюс будут развиваться инструменты автоматизации, причем всей. Сейчас на уровне IDE очень хорошо делается интеграция со всеми инструментами для тестирования. Есть плагины, которые позволяют запускать юнит-тесты на тот код, который только что был изменен. И запускать их в background-е. Получается, разработчик работает, и параллельно, как только IDE увидит, что происходит изменение в коде, она в фоне запускает юнит-тесты и дает быстрое уведомление о том, что юнит-тесты прошли или провалились.
Ещё элементарный пример, как современные средства разработки помогают сэкономить время и помогают лучше интегрироваться с тестированием, это плагин для того, чтобы делать удаленные запуски тестов, чтобы не загружать свою локальную машину и продолжать работать. Существует, к примеру, плагин для CI-системы TeamCity. Сценарий работы разработчика выглядит так: он работает, и в момент, когда он подумал: «Интересно, мои тесты проходят или нет?» — вместо того, чтобы отвлекаться, запускать все тесты у себя, ждать — он запускает их на CI-сервере.
— А для Jenkins есть такой плагин?
— Эх, к сожалению, нет.
— Жаль!
— К сожалению, для Jenkins такого плагина нет, но есть выход! В Дженкинсе просто рекомендуют использовать подход с ветками, и можно свою ветку отправить на CI. Но для этого тогда надо сначала закоммититься, отправить свою веточку и сказать: «По моей веточке прогони определенное quality gate». И вот уже эти quality gate будут являться презентацией того, что все с кодом нормально и что все прошло.
Но мир все равно движется к тому, чтобы инструменты IDE давали возможность максимально быстро получать обратную связь. Если мы сравним с тем, как было раньше, когда люди запускали только nightly builds, то тут уже возникла целая пропасть. Все будет развиваться, на мой взгляд, именно в этом направлении.
И еще одно направление, которое уже практически готово, это то, что такие инструменты, как WebDriver, будут становиться стандартными. И этот API будет становится стандартным. На текущий момент он почти уже стандарт W3C, а это обозначает, что сами производители инструментов, производители браузеров — и я надеюсь, что в будущем, если мы говорим про десктопные системы, и производители операционных систем — будут поддерживать тестируемость приложений, разработанных для этой операционной системы или для этого браузера. Потому что точно так же, как мы вкладываем тестируемость в наш код, когда пишем тест перед кодом, точно так же если иметь стандартный API для тестирования и поддерживать его, то приложения будут заведомо хорошо тестируемы.
— Надеюсь, что вскоре так и будет. Большое спасибо за интервью!
— Счастливо! Хорошего вечера!
Если хотите узнать больше о тестировании, бенчмаркинге и QA, вам скорее всего окажутся интересны следующие доклады Joker 2016 (СПб, 14-15 октября).
- Причуды Stream API
- Tracing distributed services: experiences with implementing APM for the JVM
- Мифы и факты о медленной Java
- Перформанс: Что В Имени Тебе Моём?
А если вы хотите полностью погрузиться в тему тестирования во всех его проявлениях, рекомендуем вам обратить внимание на конференцию Гейзенбаг (Москва, 10 декабря).
Пользуясь случаем, поздравляем всех тестировщиков с профессиональным праздником 09.09!
Комментарии (47)
poxu
09.09.2016 12:45+1Вопрос косвенно касающийся темы. Вы работали с Vaadin? Если да, то возможно знаете, как тестировать интерфейс, сделанны на нём? Чистый Селениум (ну или WebDriver) это умеет? На официальном сайте рекомендуют TestBench, но может можно обойтись меньшей кровью?
xpinjection
09.09.2016 12:50+2У меня нет опыта работы с Vaadin, но я когда-то разбирался как там все устроено. На уровне модульных тестов нет никаких проблем, на уровне UI WebDriver общается с браузером, поэтому ему все равно что тестировать. Но нужны будут усилия со стороны разработчиков, чтобы сделать приложение тестируемым. Обычно для веб UI это означает продуманную стратегию использования id и class атрибутов. С TestBench я дела не имел, поэтому не могу ничего сказать.
poxu
09.09.2016 13:01У нас в конторе когда-то была дискуссия о том, как вообще сделать веб UI тестируемым. Мнения разделились — часть считала, что нужно добавлять id и классы, другая часть считала, что это порождает тестный coupling приложения и тестового кода. Тогда решили, что будем искать элементы с помощью смеси css селекторов и кода. А в качестве тула выбрали TestComplete. Я был за id и против TestComplete, но, увы, не сложилось.
Собственно вопрос. Какой путь правильный — добавлять в код айдишники или предполагать, что тестирование не может влиять на разработку в такой степени?
Ещё интересно как дождаться реакции на нажатие кнопки в UI (частенько видел sleep для этого). Думаю это изрядно отражено в туториалах, я просто не изучал вопрос подробно. Но предполагаю, что надо знать к каким изменениям на странице это приведёт (например появление кнопки или картинки) и их ожидать. Такую вещь сейчас можно удобно сделать?
xpinjection
09.09.2016 13:10+2Я за подход с тестируемостью приложения, заложенную в самом приложении. Не вижу ничего плохого тут. Это тренд сейчас везде практически.
По поводу ожиданий реации уже сделано много в WebDriver. Есть механизм конфигурируемого ожидания на стороне браузера, можно ждать на стороне теста, но это короткие засыпания в цикле с проверкой при просыпании.poxu
09.09.2016 13:31+1Понял, спасибо!
А как с инструментами для тестов. У нас приложение разрабатывается на java. А тестеры автоматизированные это отдельная команда, которая использует селениум через питон. Раньше вообще javascript там был.
Питон выбран из тех соображений, что он проще джавы для новичка и можно набирать в тестирование людей, которые с программированием знакомы на начальном уровне.
Я считаю, что тестированием надо заниматься на том же языке, что и разработкой, потому что тогда разработчики смогут помочь тестерам и быстро подтянуть их до приемлемого уровня, а в дальнейшем тестировщики будут хорошим резервом кадров для команды, которая разрабатывает продукт. Или вообще станут её частью.
Что вы думаете по этому поводу?
xpinjection
09.09.2016 13:33+1Совершенно необязательно. Это даже прикольная возможность изучить другой язык на простой сравнительно задаче.
poxu
09.09.2016 13:39Он не другой, он первый. С ним, конечно, возникают вопросы — как вообще программировать правильно. Но ответить на них опытные программисты не могут, потому что пишут на другом языке :)
xpinjection
09.09.2016 13:46+2Ну питон простой достаточно и для тестирования реально выглядит неплохо.
IvanPonomarev
09.09.2016 14:12+1Дональд Кнут как-то выдал про Питон: «Only ugly languages become popular. Python is the one exception.» А подобная реплика от Кнута дорогого стоит ))
poxu
09.09.2016 14:16+1Не будем разводить холивар конечно, но Кнут это тот человек, который на вопрос
Which programming language is better? C++ or Java?
Ответил
I don't know. Which one has a better debugger?
zloddey
09.09.2016 13:56Видимо, эти программисты только делают вид, что они опытные, если не могут разобраться в Python :)
poxu
09.09.2016 14:10+1Понимать хороший код не значит уметь его писать.
Есть питон и есть автоматизированые тестировщики, которые пишут на питоне и раньше особенно ни на чём не писали и есть программисты, которые пишут на java.
Для того, чтобы насадить культуру разработки среди автоматизированных тестировщиков программистам нужно как минимум ознакомиться с best practices. Это требует времени вообще говоря. Которе у программистов не бесплатное. А джаву они уже знают и как пользоваться ей знают и IDE у них настроены и они с этими настройками легко помогут соседней команде.
Далее тестировщики немного изучат джаву на простых примерах (а код для тестов должен быть простым) и у них с программистами понимания станет больше. А если тестировщики изучат питон, то его наоборот станет меньше.
Джава — она объединяет!
zloddey
09.09.2016 15:46Немного поёрничаю:
- Что проще: тестировщику выучить свой первый язык программирования, или программисту выучить ещё один язык?
- Время разработчика, конечно, не бесплатное. А у тестировщика разве оно бесплатное?
- Лучшие практики не так уж сильно отличаются между этими двумя языками. И хорошие программисты как раз должны их знать. Если не знают, значит, их не стоит называть хорошими.
- Чем так заняты программисты, если у них нет времени вложиться в качество продукта? Клепают новые баги?
А теперь время конструктива:
Может быть, ваша проблема "Python vs. Java" — это всего лишь ложная дилемма? Вы смотрели в сторону Groovy? Он и к Java ближе, и в то же время не требует писать весь её boilerplate, что, наверно, должно облегчать вхождение для новичков. Кроме того, для тестирования с помощью WebDriver есть отличная библиотека Geb. Мы свои тесты недавно перевели на него и весьма довольны результатом.
xpinjection
09.09.2016 16:22+1Я полностью согласен, Java — не лучший выбор для функциональных тестов и работы не разработчиков. Во всех динамических языках библиотеки тестирования куда стройнее и проще в использовании. Да и нужно искать как заинтересовать разработчиков участвовать в тестировании более активно. Это один из вариантов.
poxu
09.09.2016 17:10Хотел написать по поводу ёрничанья, но не уверен, что вам будет интересен этот диалог. Если вам интересно пообщаться — напишите в ответе на коментариий — обсудим :).
А конструктив прекрасен, спасибо за конструктив.
Я скептически отношусь к Spock, потому что он по сути хорош в основном для параметризированных тестов, но нельзя не признать, что там он ну очень хорош. Надеюсь, что пятый JUnit будет справляться с этой задачей.
Geb, наверное тоже неплох и в любом случае Groovy конечно по критерию близости к джаве лучше, чем питон :)
azShoo
09.09.2016 22:03+1Большинство советов «как писать код правильно» совершенно не зависят от языка, а большинство проблем в автотестировании никак не связаны с написанием кода, больше с танцами с бубном вокруг вебдрайвера.
Я, кстати, в случае автотестов на селениуме категорически за пайтон. Тесты писать проще, излишнего кода меньше, библиотека селениума развивается быстрее, коммьюнити больше.
Vjatcheslav3345
09.09.2016 13:15Честно говоря в тестировании я — ноль, но мне хочется спросить, а нельзя ли для генерации тестов (чтобы повысить производительность) применять инструментарий, похожий на инструментарий для построения компиляторов?
https://habrahabr.ru/post/309382/
https://habrahabr.ru/post/110710/xpinjection
09.09.2016 13:32+2Много чего делается в направлении model-based testing. И инструменты появляются, но не на уровне модульных тестов.
koil
09.09.2016 16:28-8Мне интересно, Николай вообще работает на реальных проектах с реальными заказчиками или только языком на конференциях чешет? В реальности приходит заказчик и говорит, что ему надо эта фича здесь и сейчас. И вся команда пыхтит 2 следующие ночи для того что выкатить хоть минимально работающую функциональность. Я бы с удовльствием посмотрел, как Николай использовал бы TDD и писал бы «тестируемый» код в условиях жесткого дедлайна.
xpinjection
09.09.2016 16:47+10Николай работает на совершенно реальнейших проектах всю свою жизнь. Это легко отследить по профилю Николая. И только на последнем проекте Николай не пишет код, потому что ему нужно организовать работу 12 команд на проекте в 150+ человек, чтобы они на регулярной основе делали поставки работающего продукта заказчику. И да, на проекте жесточайшие дедлайны и сильное давление со стороны заказчику. При этом, большинство активно использует TDD именно по причине ускорения разработки и устранения фактора «поспешишь — людей насмешишь!».
asm0dey
10.09.2016 22:16Просто тебя нанимают люди которые знают чем ты известен :) Твой код работает даже если кажется что пишется несколько дольше.
xpinjection
10.09.2016 22:51+1Так прикол в том, что он пишется быстрее, потому что подавляющее большинство кода генерируется IDE, а не вводится руками.
asm0dey
10.09.2016 23:10+1Я понимаю, но далеко не все работодатели это понимают. Они считают что «тестировщики потом протестируют и вы поправите». И как будто бы одного цикла хватит. И таких работодателей большинство.
G-M-A-X
10.09.2016 23:51+1БОльшая часть времени тратится не на набор кода, а на чтение и мысли :)
В крайнем случае есть копипаст для построения тестов с кода. :)
pmcode
09.09.2016 22:03Скажите, пожалуйста, а насколько TDD применимо в webdev на Java и JEE в целом? Если все разложено по слоям и большинство логики приложения покрывается интеграционными тестами DAO / Repository где здесь место TDD? Все примеры, что я видел и в литературе и в докладах JUG сугубо синтетические и взяты с потолка. Реальные юзкейсы TDD для webdev (если не надо писать свой фреймворк) существуют?
asolntsev
09.09.2016 22:30+1Конечно.
TDD полезно независимо от того, энтерпрайз это или нет. В докладах простые примеры только потому, что за время доклада (40-60 минут) невозможно описать пример посложнее. И не успеешь, и люди не осилят.
Советую доклад "Интеграционные тесты — обман)" (видео).
Если вкратце — когда есть выбор, всегда лучше написать много юнит-тестов (ну и чуть-чуть интеграционных), чем много интеграционных тестов. Чем энтерпрайзнее и больше проект, тем этот принцип важнее.
xpinjection
10.09.2016 10:56+1Я неоднократно показывал как работать по TDD на уровне доступа к данным. И на весьма себе реалистичных примерах. :)
pmcode
10.09.2016 14:45Да. спасибо за ссылку, доклад отличный. Честно говоря у него есть один недостаток — такое TDD будет работать только в IntelliJ IDEA. :) Eclipse намного менее удобен в плане разработки и рефакторинга. Ну и мне кажется, что в тривиальном случае пробежаться по чеклисту, благо в нем все шаги очевидные, намного проще и быстрее чем 100500 раз запускать тест и тратить время на чтение эксепшенов. Зачем его запускать, если и так известно где и как он упадет?
Но ваш подход очень понравился в том плане, что когда первой пишешь доменную модель, обычно потом все равно приходится переписывать какие-то поля, менять их тип и т.д., а по TDD получается сначала идет добавление минимальной рабочей модели в DBMS, которое фиксируется тестом, а потом оставшиеся поля дописываются из ТЗ по мере необходимости. Это действительно удобно.zloddey
10.09.2016 21:03Зачем его запускать, если и так известно где и как он упадет?
А это в Вас говорит самоуверенность разработчика, про которую Микалай говорит в статье. Зачем, мол, писать и запускать тесты, когда я и так знаю, что, где и как будет работать? Но я бы всё же порекомендовал взять и на самом деле просто попробовать поработать так, как описывают сторонники TDD. Уверен, это даст пищу для размышлений.
По своему опыту скажу: хоть я и сам очень люблю TDD, нередко приходится бороться с соблазном "наговнячить по-быстрому без тестов, ведь тут всё просто". Преодолеваешь искушение, делаешь стандартные шаги. Бутстрап, красный тест, зелёный, зачистка, красный, зелёный, зачистка, красный, зе… — упс! — упало в неожиданном месте! Смотришь на это падение, понимаешь, что при обдумывании решения тупо проглядел один из множества частных случаев и радуешься, что всё же не зря подошёл к кодингу по-правильному. И такие моменты встречаются чаще, чем можно было бы подумать.
Подобный опыт хорошо подрезает излишнюю самоуверенность. Одна проблема: надо взять, временно умерить собственное эго и попробовать новый подход. Это самое сложное.
shishmakov
10.09.2016 00:54Не понимаю, как можно писать тесты перед тем как написать логику. Увы, не получается так.
Вы хорошо начали доклад на JPoint 2016 «Сага о том, как Java-разработчики должны тестировать свои приложения», но он был «съеден» размышлениями и ответами на вопросы. В итоге демонстарция самого TDD была неубедительна и слаба (моё мнение).
Сделаете ли вы доклад, который будет полностью посвящён написанию мини-проекта через TDD?xpinjection
10.09.2016 10:59Вот мой часовой доклад строго про TDD для уровня данных. А так обычно полный рассказ с практикой занимает на тренинге 2 дня. Поэтому в рамках конференционного доклада не всегда получается покрыть все.
chaetal
10.09.2016 10:41Test-Driven Development — это разработка (development), а не тестирование.
xpinjection
10.09.2016 11:02+1Именно так! Это подход к разработке, который характеризуется началом каждого цикла с написания теста. Идея очень простая и берется из связки «требование-реализация». Чтобы что-то реализовывать, нужно иметь требование, причем формализованное. Тест как раз такое требование выкладывает из головы в код и позволяет неоднократно его проверять по мере необходимости. А уже отталкиваясь от требования, можно разрабатывать реализацию.
chaetal
10.09.2016 20:26По большому счету, это и не тест — это спецификация, записанная в форме теста.
…А вообще я к тому, что название статьи не соответствует.IvanPonomarev
10.09.2016 20:36+1…А вообще я к тому, что название статьи не соответствует.
Нуу, разговор-то шёл не только про TDD!chaetal
10.09.2016 21:01+1Речь про несоответствие другого уровня…
Тестирование занимает особое место в работе каждого из нас. <…> Почему программисты не работают по TDD?
Может быть, в том числе и потому, что не понимают, что тестирование и TDD — это разные вещи? :) А их постоянно сваливают в кучу, что не способствует пониманию и практическому применению.
G-M-A-X
10.09.2016 23:48-2Причина очень простая: в этой идее полагаются на то, что, разработчик, когда он пишет код, одновременно думает и о реализации, и о побочных явлениях, которые могут произойти.
То есть о побочных явлениях думать не нужно?
А в тестах их тоже не нужно предусматривать? :)
Например, подход с аннотациями, где мы можем на входные параметры метода или на переменную поставить аннотацию NotNull.
А они ставятся не во время написания кода? :)
А их нельзя отключить? :)
И он сразу эти мысли выплескивает в код.
И он во время написания кода не проверяет его работоспособность? :)
Или же кто-то смотрит код на code review, и становится видно, что все методы гигантские
А при TDD все методы легкие? :)
в котором классный API
Ну так если будет ясная задача реализовать API, то разработчик его и реализует :)
Часто задача имеет undefined behaviour из-за дальнейшей правки которого все падает :)
Тут что тесты писать до, что после. :)
Ну вот классический пример — когда ты сделал три boolean-параметра в методе. И вот ты передаешь их: foo(true, false, true)
А почему такая уверенность, что их не сделают при TDD? :)
Или, например, для того чтобы вызвать один метод, тебе надо столько сетапа сделать, что ты уже забываешь, зачем ты вообще этот тест пишешь.
Может его не стоит покрывать TDD тестами? :)
И сгенерировал быстренько весь API
Херня это все. Нужно работать итерациями.
Он не пишет руками ни одной сигнатуры метода, ни одного конструктора, ни одного поля. Это все генерируется.
Для пост тестов тоже необязательно вручную писать названия методов, в крайнем случае есть копипаст :)
Потому что когда ты хочешь протестировать что-то внутри, и оно закрыто у тебя в private — это обозначает всего лишь то, что твой класс, который ты тестируешь, стал обладать слишком многими responsibility.
Стоп, стоп, стоп.
Мы TDD тест по вашему написали до кода. Откуда же внезапные private?
вынести аспект кода, который ты хочешь протестировать, в отдельный класс, задача которого будет делать именно это.
То есть нафиг ООП и инкапсуляцию? :) (сам я не адепт секты ООП)
Можно, конечно, сделать один класс, в который напихать вообще все. И он будет уметь и печатать, и сохранять базу, и трансформировать в JSON, и высчитывать какие-то алгоритмы.
При чем это к private?
А когда работа идет в команде, то такой код очень тяжело будет модифицировать, потому что все по любому чиху будут лезть в этот код и менять его.
Используйте git…
Поэтому считается, что на базу данных надо писать именно интеграционные тесты, которые будут поднимать реальный контекст, поднимать реальную базу и на этом работать.
То есть не TDD?
Но веб в основном — это работа с базой. :) Для веба TDD не подходит? :)
Но работать с настоящей базой — это очень медленно!
Если база медленная, то скорее всего она большая. :)
Если большая, то не влезет в память.
Если влезет, то она бы и на диске летала бы :)
Хотя могут быть нюансы (у меня дамп 440М базы разворачивался на винде на mysql больше часа :), sql-дамп был на много меньше 440М).
web-интерфейс
Тестирование веб-интерфейса должен писать разработчик или тестер?
Это TDD или нет?
Если мы представим себе работу приложения неким workflow-графом с переходом состояний, и на каждом из переходов состояний мы будем указывать
Это прокатит с мелким API.
Автоматическую генерацию тестовых параметров все равно нужно будет запрограммировать :)
Предполагается ли тестировать корректность работы таких генераторов? :)
Кстати, не сказано, что тесты нужны прежде всего как индикатор, что что-то сломалось при изменениях. Отсутствие багов вообще они не гарантируют. :)xpinjection
11.09.2016 22:32+2То есть о побочных явлениях думать не нужно?
А в тестах их тоже не нужно предусматривать? :)
Просто очень тяжело одновременно писать код и думать. Мозг переключатся. Альтернативой является записывать в блокнот, но на практике мало кто так делает. В тестах предусматривать обязательно, но для этого нужно обладать знаниями тест дизайна.
А они ставятся не во время написания кода? :)
А их нельзя отключить? :)
Так проблема в том, что они по умолчанию выключены. А расставлять мне лично больше всего нравится в рамках написания валидационных тестов на исключительные ситуации с параметрами.
И он во время написания кода не проверяет его работоспособность? :)
А как же им проверить, если не пишутся тесты? Написал метод новый и запускать все приложение для проверки? Или писать main, тело которого будет практически тем же тестом? Это неэффективные техники.
А при TDD все методы легкие? :)
При правильном TDD да, потому что заложен в процесс обязательный шаг рефакторинга. И только рукожопство может помешать на этом этапе сделать методы вменяемыми по размеру и сложности под прикрытием тестов.
Ну так если будет ясная задача реализовать API, то разработчик его и реализует :)
Часто задача имеет undefined behaviour из-за дальнейшей правки которого все падает :)
Тут что тесты писать до, что после. :)
Речь не о глобальном API приложения, а на уровне класса (его публичного поведения) или связки классов. В тестах вы смотрите на еще не существующий код с точки зрения вызывающего и делаете правильный удобный API.
А почему такая уверенность, что их не сделают при TDD? :)
Потому что ты в тесте написал «true, false, true» и сразу очевидно какая фигня на выходе получается. А просто по сигнатуре метода эта боль так не чувствуется.
Стоп, стоп, стоп.
Мы TDD тест по вашему написали до кода. Откуда же внезапные private?
Вопрос был задан на тему существующего кода, который разрабатывался не по TDD.
То есть нафиг ООП и инкапсуляцию? :) (сам я не адепт секты ООП)
Нет никакого нарушения ООП или инкапсуляции. Класс должен отвечать за что-то одно. Если он начал делать больше, то явно нужно это делегировать кому-то. Так появляется новый класс, с которым старый взаимодействует.
При чем это к private?
Потому что обычно для уменьшения сложности метода люди выделяют приватные методы. В них выходит большая часть логики. В итоге класс как таковой перегружен логикой, но с точки зрения размеров и сложности методов все отлично.
Используйте git…
Так его и использует большинство. Вот только как git может помочь при логическом изменении несколькими людьми одного и того же кода? Мержиться придется чаще, а это чревато ошибками.
То есть не TDD?
Но веб в основном — это работа с базой. :) Для веба TDD не подходит? :)
Почему не TDD? TDD — это стиль разработки, при котором вы отталкиваетесь от тестов и идете к реализации. И совершенно не важно веб это или база данных.
Если база медленная, то скорее всего она большая. :)
Если большая, то не влезет в память.
Если влезет, то она бы и на диске летала бы :)
Просто набор оторванных от реальности утверждений. Доказывать вам обратное на реальных примерах слишком затратно по времени. Поэтому просто поверьте на слово. :)
Тестирование веб-интерфейса должен писать разработчик или тестер?
Это TDD или нет?
Может быть и тот и другой, причем как вместе так и поотдельности. Все зависит от навыков конкретных людей и поставленного у вас процесса разработки. Ну а решить TDD или нет достаточно просто. Если тесты пишутся до реализации и используются разработчиками при ее создании, то это TDD. Если что-то из этого неверно у вас, то не TDD.
Это прокатит с мелким API.
Автоматическую генерацию тестовых параметров все равно нужно будет запрограммировать :)
Предполагается ли тестировать корректность работы таких генераторов? :)
Это огромная область, где есть много исследований вплоть до научных работ. Сейчас делается ряд инструментов, который помогает использовать идею на практике. Детальное описание выходит за рамки этой статьи.
Кстати, не сказано, что тесты нужны прежде всего как индикатор, что что-то сломалось при изменениях. Отсутствие багов вообще они не гарантируют. :)
Отсутствие багов вообще никто и никогда не гарантирует. Ваша задача свести вероятность к минимуму и уменьшить потенциальную критичность дефекта.G-M-A-X
11.09.2016 23:44Просто очень тяжело одновременно писать код и думать.
Писать код не думая? :)
В тестах предусматривать обязательно, но для этого нужно обладать знаниями тест дизайна.
Если мы можем предусмотреть в тесте, что тоже вряд ли мы сразу все предусмотрим, то почему не можем в коде? :)
Тест должен писать тот же разработчик или другой?
Так проблема в том, что они по умолчанию выключены. А расставлять мне лично больше всего нравится в рамках написания валидационных тестов на исключительные ситуации с параметрами.
Вы об ассертах в тестах?
А тут предлагали аннотации в коде :)
А как же им проверить, если не пишутся тесты? Написал метод новый и запускать все приложение для проверки? Или писать main, тело которого будет практически тем же тестом? Это неэффективные техники.
В Java может и да. Да и вряд ли нужно запускать все приложение для проверки одного метода.
В PHP нет, тут легко вызвать нужный метод.
При правильном TDD да, потому что заложен в процесс обязательный шаг рефакторинга.
То есть: заложили в TDD жирный метод -> реализовали метод -> он жирный -> зарефакторили тесты -> зарефакторили метод.
Почему нельзя: реализовали метод -> реализовали тест -> метод жирный -> зарефакторили метод -> зарефакторили тесты?
Речь не о глобальном API приложения
Так и я не о глобальном API.
Повторю:
Часто задача имеет undefined behaviour из-за дальнейшей правки которого все падает :)
Тут что тесты писать до, что после. :)
Потому что ты в тесте написал «true, false, true» и сразу очевидно какая фигня на выходе получается. А просто по сигнатуре метода эта боль так не чувствуется.
Мы можем решить при написании теста, что это нормально. :)
Вопрос был задан на тему существующего кода, который разрабатывался не по TDD.
Я понял, что мы только хотим сделать, ну ладно:
Вот, например, хочется сделать private какой-нибудь, а приходится его открывать
Нет никакого нарушения ООП или инкапсуляции.
Был класс с приватными членами, а стал класс без приватных членов, использующий другой класс без приватных членов…
Потому что обычно для уменьшения сложности метода люди выделяют приватные методы. В них выходит большая часть логики. В итоге класс как таковой перегружен логикой, но с точки зрения размеров и сложности методов все отлично.
Наличие приватных свойств не означает, что там логика из разных степей.
Но если нужно избавляться от private, то Вы таки против инкапсуляции, которая одна из священных коров ООП (не для меня)? :)
Вот только как git может помочь при логическом изменении несколькими людьми одного и того же кода?
Он попытается все сам решить у спросит вас при конфликте.
Одновременно править один и тот же код не стоит.
Мержиться придется чаще, а это чревато ошибками.
Чем чаще мерджится, тем меньше конликтов. :)
И каким образом тесты до / тесты после защищают от последующих правок одного и того же кода в той же ветке / в разных ветках?
Никаким.
Почему не TDD?
Так Вы сами сказали, что интеграционные тесты. Или они тоже пишутся до кода?
Просто набор оторванных от реальности утверждений. Доказывать вам обратное на реальных примерах слишком затратно по времени. Поэтому просто поверьте на слово. :)
Так я ж добавил, что не все так однозначно:
Хотя могут быть нюансы (у меня дамп 440М базы разворачивался на винде на mysql больше часа :), sql-дамп был на много меньше 440М).
Если тесты пишутся до реализации и используются разработчиками при ее создании, то это TDD. Если что-то из этого неверно у вас, то не TDD.
Но тесты, написанные после, это ж тоже не плохо? :)
Если все делают разработчики, то тестировщики не нужны? :)
Отсутствие багов вообще никто и никогда не гарантирует.
Мне просто кажется, что нужно делать больше акцент на:
тесты нужны прежде всего как индикатор, что что-то сломалось при изменениях
А также:
Как мы можем заранее при написании теста знать, что нам понадобятся нижестоящие классы?
Может все же использовать итерации? Пописали чуток тестов (кода), потом замутили код (тесты)?
Стоит ли, чтобы тесты и код писали разные люди (особенно если тесты нужно писать целиком, без итераций): разработчик / разработчик или тестировщик / разработчик?
Возникали ли у Вас проблемы с поддержкой актуальности тестов из-за дальнейшего изменения функционала?
Что первым нужно менять: тест или код?
Не кажется ли Вам, что в большинстве случаев тест будет написан так, что не сможет выявить закравшийся баг, то есть критические точки изначально не будут учтены ни в коде, ни в тесте. Или все же не стоит позиционировать тесты, как защиту от багов?
Тесты нужны всем?
Или на мелочных проектах можно и без них?
Наверное, маст хев наступает, когда большая кодовая база / большая команда?
Спасибо за ответ.
Вообще я за тесты на больших проектах, но не до кода, а параллельно (или их пишет кто-то другой).
Пока проверяю работу во время написания кода. :)xpinjection
11.09.2016 23:53+2Вы назадавали вопросов на еще несколько статей. Писать их в комментариях мне кажется нецелесообразным. К тому же, попробуйте просто вдуматься в данные мной ответы, а не сразу же писать свой новый вопрос. Просто вот эти ваши уточнения кидают тень на вообще суть понимания вами моих ответов:
Писать код не думая? :)
Если мы можем предусмотреть в тесте, что тоже вряд ли мы сразу все предусмотрим, то почему не можем в коде? :)
Вы об ассертах в тестах?
То есть: заложили в TDD жирный метод -> реализовали метод -> он жирный -> зарефакторили тесты -> зарефакторили метод.
Мы можем решить при написании теста, что это нормально. :)
Был класс с приватными членами, а стал класс без приватных членов, использующий другой класс без приватных членов…
Но если нужно избавляться от private, то Вы таки против инкапсуляции, которая одна из священных коров ООП (не для меня)? :)
Он попытается все сам решить у спросит вас при конфликте.
Одновременно править один и тот же код не стоит.
Чем чаще мерджится, тем меньше конликтов. :)
Если все делают разработчики, то тестировщики не нужны? :)
Xandrmoro
В том числе из-за этого подхода я против этого вашего тдд. Писать хорошо надо сразу, потому что закрывать потом технический долг никто не хочет. Проблема не в неверно работающем коде, а в неподдерживаемом кода, и тдд эту проблему решает почти никак (да, иногда поможет в рефакторинге, но чаще только увеливиает работу в разы).
xpinjection
Речь не идет о «писать сразу ужасно преужасно», а о «фокусироваться на решении проблемы, а уже потом с решенной проблемой улучшать дизайн». И фаза рефакторинга в ТДД обязательна, вы не переходите к следующему тесту пока не привели код к хорошему дизайну. И накопить технический долг не удастся, потому что объем кода для одного теста сравнительно небольшой. А по поводу технического долга как раз сегодня написал статейку со ссылкой на полезное видео.
zloddey
По своему опыту скажу, что TDD стимулирует не "писать сразу хорошо", а "писать минимальную работающую реализацию". В таком случае неподдерживаемому коду появиться сложнее. Его просто не пишешь! При работе без тестов часто возникает соблазн написать какой-то кусок кода по принципу "а вдруг пригодится". И спустя какое-то время такой код уже невозможно отличить от необходимого, а поддерживать надо наравне с необходимым. При TDD мы стараемся писать только тот код, который даёт пройти текущий падающий тест. Поэтому для кода-на-всякий-случай есть две возможности: либо его не писать вообще, либо придумать конкретный новый тест-кейс, для которого этот код нужен.
Решать проблему с техническим долгом, опять-таки, с тестами намного легче. Если у нас есть достаточный набор тестов на API модуля (класса), то можно уже куда более вольно переделывать кишки этого модуля. Уходит боязнь что-то сломать, потому что в тестах жёстко задано, как себя должен вести этот модуль. Похачили кишки — запустили тесты — увидели падение — нашли проблемное место — исправили — запустили тесты — и так далее. А без тестов приходится либо действовать наугад, либо строить в голове сложную модель работы модуля и пытаться понять, может ли что-то сломаться при изменениях.
Другой вопрос, считаете ли Вы сами тесты "неподдерживаемым кодом". У некоторых разработчиков есть такой грех, это факт. Код мы пишем, на существующие тесты забиваем, тесты ломаются — нам пофиг, пускай разгребает кто-то другой. Очень сложно работать по TDD, когда в команде есть такой деструктивный элемент. Приходится не только свою работу делать, но и за него всё чинить. Но здесь тоже известны стандартные способы решения: внедрение CI, воспитательная работа, практика code retreat и т.п. Если человек адекватный, то со временем он перевоспитывается.