Комментарии 48
Когда ты работаешь по TDD, то ты написал тест, и теперь твоя задача просто в том, чтобы он заработал. Твоя задача не сделать суперклассный дизайн. Задача сделать суперклассный дизайн возникает уже после того, как код заработал. Ты потом смотришь на него и говоришь: «Вот я простое решение написал. А можно его как-то сделать красивее? Можно сделать его как-то более элегантным? Или reusable?» И если нет — ну окей. Оно работает и работает, поехали дальше.
В том числе из-за этого подхода я против этого вашего тдд. Писать хорошо надо сразу, потому что закрывать потом технический долг никто не хочет. Проблема не в неверно работающем коде, а в неподдерживаемом кода, и тдд эту проблему решает почти никак (да, иногда поможет в рефакторинге, но чаще только увеливиает работу в разы).
По своему опыту скажу, что TDD стимулирует не "писать сразу хорошо", а "писать минимальную работающую реализацию". В таком случае неподдерживаемому коду появиться сложнее. Его просто не пишешь! При работе без тестов часто возникает соблазн написать какой-то кусок кода по принципу "а вдруг пригодится". И спустя какое-то время такой код уже невозможно отличить от необходимого, а поддерживать надо наравне с необходимым. При TDD мы стараемся писать только тот код, который даёт пройти текущий падающий тест. Поэтому для кода-на-всякий-случай есть две возможности: либо его не писать вообще, либо придумать конкретный новый тест-кейс, для которого этот код нужен.
Решать проблему с техническим долгом, опять-таки, с тестами намного легче. Если у нас есть достаточный набор тестов на API модуля (класса), то можно уже куда более вольно переделывать кишки этого модуля. Уходит боязнь что-то сломать, потому что в тестах жёстко задано, как себя должен вести этот модуль. Похачили кишки — запустили тесты — увидели падение — нашли проблемное место — исправили — запустили тесты — и так далее. А без тестов приходится либо действовать наугад, либо строить в голове сложную модель работы модуля и пытаться понять, может ли что-то сломаться при изменениях.
Другой вопрос, считаете ли Вы сами тесты "неподдерживаемым кодом". У некоторых разработчиков есть такой грех, это факт. Код мы пишем, на существующие тесты забиваем, тесты ломаются — нам пофиг, пускай разгребает кто-то другой. Очень сложно работать по TDD, когда в команде есть такой деструктивный элемент. Приходится не только свою работу делать, но и за него всё чинить. Но здесь тоже известны стандартные способы решения: внедрение CI, воспитательная работа, практика code retreat и т.п. Если человек адекватный, то со временем он перевоспитывается.
Вопрос косвенно касающийся темы. Вы работали с Vaadin? Если да, то возможно знаете, как тестировать интерфейс, сделанны на нём? Чистый Селениум (ну или WebDriver) это умеет? На официальном сайте рекомендуют TestBench, но может можно обойтись меньшей кровью?
У нас в конторе когда-то была дискуссия о том, как вообще сделать веб UI тестируемым. Мнения разделились — часть считала, что нужно добавлять id и классы, другая часть считала, что это порождает тестный coupling приложения и тестового кода. Тогда решили, что будем искать элементы с помощью смеси css селекторов и кода. А в качестве тула выбрали TestComplete. Я был за id и против TestComplete, но, увы, не сложилось.
Собственно вопрос. Какой путь правильный — добавлять в код айдишники или предполагать, что тестирование не может влиять на разработку в такой степени?
Ещё интересно как дождаться реакции на нажатие кнопки в UI (частенько видел sleep для этого). Думаю это изрядно отражено в туториалах, я просто не изучал вопрос подробно. Но предполагаю, что надо знать к каким изменениям на странице это приведёт (например появление кнопки или картинки) и их ожидать. Такую вещь сейчас можно удобно сделать?
По поводу ожиданий реации уже сделано много в WebDriver. Есть механизм конфигурируемого ожидания на стороне браузера, можно ждать на стороне теста, но это короткие засыпания в цикле с проверкой при просыпании.
Понял, спасибо!
А как с инструментами для тестов. У нас приложение разрабатывается на java. А тестеры автоматизированные это отдельная команда, которая использует селениум через питон. Раньше вообще javascript там был.
Питон выбран из тех соображений, что он проще джавы для новичка и можно набирать в тестирование людей, которые с программированием знакомы на начальном уровне.
Я считаю, что тестированием надо заниматься на том же языке, что и разработкой, потому что тогда разработчики смогут помочь тестерам и быстро подтянуть их до приемлемого уровня, а в дальнейшем тестировщики будут хорошим резервом кадров для команды, которая разрабатывает продукт. Или вообще станут её частью.
Что вы думаете по этому поводу?
Он не другой, он первый. С ним, конечно, возникают вопросы — как вообще программировать правильно. Но ответить на них опытные программисты не могут, потому что пишут на другом языке :)
Видимо, эти программисты только делают вид, что они опытные, если не могут разобраться в Python :)
Понимать хороший код не значит уметь его писать.
Есть питон и есть автоматизированые тестировщики, которые пишут на питоне и раньше особенно ни на чём не писали и есть программисты, которые пишут на java.
Для того, чтобы насадить культуру разработки среди автоматизированных тестировщиков программистам нужно как минимум ознакомиться с best practices. Это требует времени вообще говоря. Которе у программистов не бесплатное. А джаву они уже знают и как пользоваться ей знают и IDE у них настроены и они с этими настройками легко помогут соседней команде.
Далее тестировщики немного изучат джаву на простых примерах (а код для тестов должен быть простым) и у них с программистами понимания станет больше. А если тестировщики изучат питон, то его наоборот станет меньше.
Джава — она объединяет!
Немного поёрничаю:
- Что проще: тестировщику выучить свой первый язык программирования, или программисту выучить ещё один язык?
- Время разработчика, конечно, не бесплатное. А у тестировщика разве оно бесплатное?
- Лучшие практики не так уж сильно отличаются между этими двумя языками. И хорошие программисты как раз должны их знать. Если не знают, значит, их не стоит называть хорошими.
- Чем так заняты программисты, если у них нет времени вложиться в качество продукта? Клепают новые баги?
А теперь время конструктива:
Может быть, ваша проблема "Python vs. Java" — это всего лишь ложная дилемма? Вы смотрели в сторону Groovy? Он и к Java ближе, и в то же время не требует писать весь её boilerplate, что, наверно, должно облегчать вхождение для новичков. Кроме того, для тестирования с помощью WebDriver есть отличная библиотека Geb. Мы свои тесты недавно перевели на него и весьма довольны результатом.
Хотел написать по поводу ёрничанья, но не уверен, что вам будет интересен этот диалог. Если вам интересно пообщаться — напишите в ответе на коментариий — обсудим :).
А конструктив прекрасен, спасибо за конструктив.
Я скептически отношусь к Spock, потому что он по сути хорош в основном для параметризированных тестов, но нельзя не признать, что там он ну очень хорош. Надеюсь, что пятый JUnit будет справляться с этой задачей.
Geb, наверное тоже неплох и в любом случае Groovy конечно по критерию близости к джаве лучше, чем питон :)
Я, кстати, в случае автотестов на селениуме категорически за пайтон. Тесты писать проще, излишнего кода меньше, библиотека селениума развивается быстрее, коммьюнити больше.
Честно говоря в тестировании я — ноль, но мне хочется спросить, а нельзя ли для генерации тестов (чтобы повысить производительность) применять инструментарий, похожий на инструментарий для построения компиляторов?
https://habrahabr.ru/post/309382/
https://habrahabr.ru/post/110710/
Просто тебя нанимают люди которые знают чем ты известен :) Твой код работает даже если кажется что пишется несколько дольше.
Я понимаю, но далеко не все работодатели это понимают. Они считают что «тестировщики потом протестируют и вы поправите». И как будто бы одного цикла хватит. И таких работодателей большинство.
В крайнем случае есть копипаст для построения тестов с кода. :)
TDD полезно независимо от того, энтерпрайз это или нет. В докладах простые примеры только потому, что за время доклада (40-60 минут) невозможно описать пример посложнее. И не успеешь, и люди не осилят.
Советую доклад "Интеграционные тесты — обман)" (видео).
Если вкратце — когда есть выбор, всегда лучше написать много юнит-тестов (ну и чуть-чуть интеграционных), чем много интеграционных тестов. Чем энтерпрайзнее и больше проект, тем этот принцип важнее.
Зачем его запускать, если и так известно где и как он упадет?
А это в Вас говорит самоуверенность разработчика, про которую Микалай говорит в статье. Зачем, мол, писать и запускать тесты, когда я и так знаю, что, где и как будет работать? Но я бы всё же порекомендовал взять и на самом деле просто попробовать поработать так, как описывают сторонники TDD. Уверен, это даст пищу для размышлений.
По своему опыту скажу: хоть я и сам очень люблю TDD, нередко приходится бороться с соблазном "наговнячить по-быстрому без тестов, ведь тут всё просто". Преодолеваешь искушение, делаешь стандартные шаги. Бутстрап, красный тест, зелёный, зачистка, красный, зелёный, зачистка, красный, зе… — упс! — упало в неожиданном месте! Смотришь на это падение, понимаешь, что при обдумывании решения тупо проглядел один из множества частных случаев и радуешься, что всё же не зря подошёл к кодингу по-правильному. И такие моменты встречаются чаще, чем можно было бы подумать.
Подобный опыт хорошо подрезает излишнюю самоуверенность. Одна проблема: надо взять, временно умерить собственное эго и попробовать новый подход. Это самое сложное.
Вы хорошо начали доклад на JPoint 2016 «Сага о том, как Java-разработчики должны тестировать свои приложения», но он был «съеден» размышлениями и ответами на вопросы. В итоге демонстарция самого TDD была неубедительна и слаба (моё мнение).
Сделаете ли вы доклад, который будет полностью посвящён написанию мини-проекта через TDD?
…А вообще я к тому, что название статьи не соответствует.
…А вообще я к тому, что название статьи не соответствует.
Нуу, разговор-то шёл не только про TDD!
Тестирование занимает особое место в работе каждого из нас. <…> Почему программисты не работают по TDD?Может быть, в том числе и потому, что не понимают, что тестирование и TDD — это разные вещи? :) А их постоянно сваливают в кучу, что не способствует пониманию и практическому применению.
Причина очень простая: в этой идее полагаются на то, что, разработчик, когда он пишет код, одновременно думает и о реализации, и о побочных явлениях, которые могут произойти.
То есть о побочных явлениях думать не нужно?
А в тестах их тоже не нужно предусматривать? :)
Например, подход с аннотациями, где мы можем на входные параметры метода или на переменную поставить аннотацию 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.
Автоматическую генерацию тестовых параметров все равно нужно будет запрограммировать :)
Предполагается ли тестировать корректность работы таких генераторов? :)
Кстати, не сказано, что тесты нужны прежде всего как индикатор, что что-то сломалось при изменениях. Отсутствие багов вообще они не гарантируют. :)
То есть о побочных явлениях думать не нужно?
А в тестах их тоже не нужно предусматривать? :)
Просто очень тяжело одновременно писать код и думать. Мозг переключатся. Альтернативой является записывать в блокнот, но на практике мало кто так делает. В тестах предусматривать обязательно, но для этого нужно обладать знаниями тест дизайна.
А они ставятся не во время написания кода? :)
А их нельзя отключить? :)
Так проблема в том, что они по умолчанию выключены. А расставлять мне лично больше всего нравится в рамках написания валидационных тестов на исключительные ситуации с параметрами.
И он во время написания кода не проверяет его работоспособность? :)
А как же им проверить, если не пишутся тесты? Написал метод новый и запускать все приложение для проверки? Или писать 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.
Автоматическую генерацию тестовых параметров все равно нужно будет запрограммировать :)
Предполагается ли тестировать корректность работы таких генераторов? :)
Это огромная область, где есть много исследований вплоть до научных работ. Сейчас делается ряд инструментов, который помогает использовать идею на практике. Детальное описание выходит за рамки этой статьи.
Кстати, не сказано, что тесты нужны прежде всего как индикатор, что что-то сломалось при изменениях. Отсутствие багов вообще они не гарантируют. :)
Отсутствие багов вообще никто и никогда не гарантирует. Ваша задача свести вероятность к минимуму и уменьшить потенциальную критичность дефекта.
Просто очень тяжело одновременно писать код и думать.
Писать код не думая? :)
В тестах предусматривать обязательно, но для этого нужно обладать знаниями тест дизайна.
Если мы можем предусмотреть в тесте, что тоже вряд ли мы сразу все предусмотрим, то почему не можем в коде? :)
Тест должен писать тот же разработчик или другой?
Так проблема в том, что они по умолчанию выключены. А расставлять мне лично больше всего нравится в рамках написания валидационных тестов на исключительные ситуации с параметрами.
Вы об ассертах в тестах?
А тут предлагали аннотации в коде :)
А как же им проверить, если не пишутся тесты? Написал метод новый и запускать все приложение для проверки? Или писать main, тело которого будет практически тем же тестом? Это неэффективные техники.
В Java может и да. Да и вряд ли нужно запускать все приложение для проверки одного метода.
В PHP нет, тут легко вызвать нужный метод.
При правильном TDD да, потому что заложен в процесс обязательный шаг рефакторинга.
То есть: заложили в TDD жирный метод -> реализовали метод -> он жирный -> зарефакторили тесты -> зарефакторили метод.
Почему нельзя: реализовали метод -> реализовали тест -> метод жирный -> зарефакторили метод -> зарефакторили тесты?
Речь не о глобальном API приложения
Так и я не о глобальном API.
Повторю:
Часто задача имеет undefined behaviour из-за дальнейшей правки которого все падает :)
Тут что тесты писать до, что после. :)
Потому что ты в тесте написал «true, false, true» и сразу очевидно какая фигня на выходе получается. А просто по сигнатуре метода эта боль так не чувствуется.
Мы можем решить при написании теста, что это нормально. :)
Вопрос был задан на тему существующего кода, который разрабатывался не по TDD.
Я понял, что мы только хотим сделать, ну ладно:
Вот, например, хочется сделать private какой-нибудь, а приходится его открывать
Нет никакого нарушения ООП или инкапсуляции.
Был класс с приватными членами, а стал класс без приватных членов, использующий другой класс без приватных членов…
Потому что обычно для уменьшения сложности метода люди выделяют приватные методы. В них выходит большая часть логики. В итоге класс как таковой перегружен логикой, но с точки зрения размеров и сложности методов все отлично.
Наличие приватных свойств не означает, что там логика из разных степей.
Но если нужно избавляться от private, то Вы таки против инкапсуляции, которая одна из священных коров ООП (не для меня)? :)
Вот только как git может помочь при логическом изменении несколькими людьми одного и того же кода?
Он попытается все сам решить у спросит вас при конфликте.
Одновременно править один и тот же код не стоит.
Мержиться придется чаще, а это чревато ошибками.
Чем чаще мерджится, тем меньше конликтов. :)
И каким образом тесты до / тесты после защищают от последующих правок одного и того же кода в той же ветке / в разных ветках?
Никаким.
Почему не TDD?
Так Вы сами сказали, что интеграционные тесты. Или они тоже пишутся до кода?
Просто набор оторванных от реальности утверждений. Доказывать вам обратное на реальных примерах слишком затратно по времени. Поэтому просто поверьте на слово. :)
Так я ж добавил, что не все так однозначно:
Хотя могут быть нюансы (у меня дамп 440М базы разворачивался на винде на mysql больше часа :), sql-дамп был на много меньше 440М).
Если тесты пишутся до реализации и используются разработчиками при ее создании, то это TDD. Если что-то из этого неверно у вас, то не TDD.
Но тесты, написанные после, это ж тоже не плохо? :)
Если все делают разработчики, то тестировщики не нужны? :)
Отсутствие багов вообще никто и никогда не гарантирует.
Мне просто кажется, что нужно делать больше акцент на:
тесты нужны прежде всего как индикатор, что что-то сломалось при изменениях
А также:
Как мы можем заранее при написании теста знать, что нам понадобятся нижестоящие классы?
Может все же использовать итерации? Пописали чуток тестов (кода), потом замутили код (тесты)?
Стоит ли, чтобы тесты и код писали разные люди (особенно если тесты нужно писать целиком, без итераций): разработчик / разработчик или тестировщик / разработчик?
Возникали ли у Вас проблемы с поддержкой актуальности тестов из-за дальнейшего изменения функционала?
Что первым нужно менять: тест или код?
Не кажется ли Вам, что в большинстве случаев тест будет написан так, что не сможет выявить закравшийся баг, то есть критические точки изначально не будут учтены ни в коде, ни в тесте. Или все же не стоит позиционировать тесты, как защиту от багов?
Тесты нужны всем?
Или на мелочных проектах можно и без них?
Наверное, маст хев наступает, когда большая кодовая база / большая команда?
Спасибо за ответ.
Вообще я за тесты на больших проектах, но не до кода, а параллельно (или их пишет кто-то другой).
Пока проверяю работу во время написания кода. :)
Писать код не думая? :)
Если мы можем предусмотреть в тесте, что тоже вряд ли мы сразу все предусмотрим, то почему не можем в коде? :)
Вы об ассертах в тестах?
То есть: заложили в TDD жирный метод -> реализовали метод -> он жирный -> зарефакторили тесты -> зарефакторили метод.
Мы можем решить при написании теста, что это нормально. :)
Был класс с приватными членами, а стал класс без приватных членов, использующий другой класс без приватных членов…
Но если нужно избавляться от private, то Вы таки против инкапсуляции, которая одна из священных коров ООП (не для меня)? :)
Он попытается все сам решить у спросит вас при конфликте.
Одновременно править один и тот же код не стоит.
Чем чаще мерджится, тем меньше конликтов. :)
Если все делают разработчики, то тестировщики не нужны? :)
Так там как раз итерационные TDD.
Я всегда так делаю (и, наверное, другие), просто у меня не остается кода теста, он уходит в код приложения.
:)
Сферическое тестирование в вакууме: Как есть, как должно быть, как будет