Как начать писать тесты за 10 шагов по 10 минут

  • Tutorial
Дайте-ка угадаю: вы согласны с тем, что писать тесты — это хорошо. Это повышает надежность системы, ускоряет разработку, проект с хорошим тестовым покрытием поддерживать легко и приятно, а TDD — это вообще почти идеал процесса разработки. Но не у вас в проекте. То есть, оно клёво, но, к сожалению, сейчас столько работы — просто завал. Куча задач, одних только критических багов — два десятка, плюс надо срочно дописать этот модуль и еще написать письмо заказчику… Так что тесты, наверное, будем прикручивать уже в конце, если время останется. Или в следующем проекте. Нет, ну там точно полегче будет. Скорее всего.

Как, узнали ситуацию?

Так вот — чушь всё это. Сфера ИТ — бесконечна, как вселенная, куча работы будет всегда. Можно или начать писать тесты прямо сейчас, или не сделать этого никогда. Я тут набросал короткий план, как начать это делать за 10 шагов, по шагу в день, по 10 минут на шаг. И когда я говорю «10 минут» я имею в виду не «3 с половиной часа» и не «ну сколько-то времени, лучше побольше», а именно 600 секунд. Если у вас нету в день 600 секунд свободного времени — срочно меняйте проект, работу, профессию, страну проживания (нужное подчеркнуть), потому что это не жизнь, а каторга какая-то. Поехали.


1. Выбираем фреймворк для тестов

Не вздумайте начинать писать собственный фреймворк с нуля — оно вам надо? Тратить неделю на выбор оптимального фреймворка (да, я видел такую оценку времени на это в планах) — тоже глупо. Вот вам рецепт: набирайте в Гугле best test framework for %language% site:stackoverflow.com. Открываете первые 5 ссылок. Закрываете те из них, где рейтинг вопроса или первого ответа около нуля. Из оставшихся вкладок можно смело брать любой рекомендованный фреймворк из первой тройки с максимальным рейтингом. С вероятностью в 99.5% он вам подойдет. Поскольку на данный шаг вы пока потратили минуты 3, то оставшиеся 7 можно потратить на то, чтобы перейти на сайт фреймворка и посмотреть примеры его использования. Скорее всего, там всё будет просто и понятно (иначе он не был бы в топе рекомендаций). Но если вдруг нет — выберите другой по тому же алгоритму.

2. Пишем Hello world!

Написать Hello, world! нам раз плюнуть. Вот, например, на С++.
Hello world!
#include <iostream>

using namespace std;

int main()
{
	cout << "Hello world!" << endl;
	return 0;
}


А теперь сделаем две вещи.
Во-первых, вынесем генерацию выводимого текста в отдельные функции. Да, в две. Это для того, чтобы потом их можно было тестировать.
Hello world! после рефакторинга
#include <iostream>
#include <string>

using namespace std;

string GetHello()
{
	return "Hello";
}

string GetAdressat(string adressat)
{
	return adressat;
}

int main()
{
	cout << GetHello() + " " + GetAdressat("world") + "!" << endl;
	return 0;
}


Во-вторых, вынесем написанные функции куда-нибудь из данного файла. В зависимости от подхода и применяемого языка это могут быть просто отдельные файлы кода или библиотека. Это нужно для того, чтобы потом эти функции вызывать из тестов.
У нас будет так:

HelloFunctions.h
#include <string>
using namespace std;

string GetHello();
string GetAdressat(string adressat);


HelloFunctions.cpp
#include "HelloFunctions.h"

string GetHello()
{
	return "Hello";
}

string GetAdressat(string adressat)
{
	return adressat;
}


HelloWorld.cpp
#include <iostream>
#include "HelloFunctions.h"

using namespace std;

int main()
{
	cout << GetHello() + " " + GetAdressat("world") + "!" << endl;
	return 0;
}



3. Подключаем фреймворк к Hello world!

О подключении фреймворка к проекту наверняка очень хорошо написано на сайте фреймворка. Или на stackoverflow. Или на Хабре. Вот я, к примеру, когда-то описывал подключение Google Test. Обычно всё сводится к созданию нового проекта консольного исполняемого приложения (в скриптовых языках — отдельного скрипта), подключению к нему фрейворка парой include (import\using), подключению к проекту тестируемого кода (включением самих файлов с кодом или подключением библиотеки) — ну и всё. Если вы не верите, что этот шаг можно сделать за 10 минут — откройте Youtube, напишите в поиск название своего фреймворка и пронаблюдайте 20 видеороликов примерно одинакового содержимого, которые это доказывают.

4. Разбираемся с возможностями фреймворка

Для начала нам нужно выяснить:
  • Как написать один юнит-тест
  • Как запустить юнит-тесты

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

Вот, к примеру, пару тестов для нашего Hello world! на упомянутом выше Google Test:
#include "HelloFunctions.h"
#include "gtest/gtest.h"

class CHelloTest : public ::testing::Test {
};

TEST_F(CHelloTest, CheckGetHello) 
{
    ASSERT_TRUE(GetHello() == "Hello");
}

TEST_F(CHelloTest, GetAdressat) 
{
    ASSERT_TRUE(GetAdressat("world") == "world");
	ASSERT_FALSE(GetAdressat("not world") == "world");
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}


5. Подключаем фреймворк к настоящему проекту

Мы уже умеем подключать фреймворк к проекту. Помните, делали на шаге №3? Всё получилось. Теперь давайте сделаем это для боевого проекта. Положите все необходимые файлы фреймворка себе под SVN\Git\TFS\чего-у-вас-там. Сделайте тестовый проект. Подключите к нему фреймворк. Включите сборку тестового проект в процесс сборки вашего продукта. Проверьте сборку в дебаг и релиз-конфигурациях. Комитните тестовый проект, запустите сборку на билд-сервере. Всё должно быть ок. Не нагружайте пока ваших коллег появлением тестового проекта — во-первых, вы ничего не сломали, во-вторых, хвастаться вам тоже пока нечем.

6. Тестируем что-нибудь простое

Вы помните, каким образом мы выше вынесли из Hello world! часть функционала во внешний код? Обратите внимание, какими получились эти функции: они не зависят ни от глобальных переменных, ни от состояния каких-то объектов, ни от внешних данных из файлов или баз данных. Резальтат зависит только от переданных аргументов. Найдите в своём проекте что-то аналогичное. Наверняка ведь у вас есть какие-нибудь функции конвертации чего-то куда-то, сериализации\десериализации, упаковки\распаковки, шифрования\дешифрования и т.д. Не думайте пока о том, насколько нужный и полезный функционал вы тестируете. Ваша задача — написать простой тест, но для боевого проекта. Запустить, увидеть «1 тест успешно пройден».

Кстати, именно на этом этапе очень часто к скептикам приходит озарения. Вдруг оказывается, что самый простой тест, на самую элементарную функциональность — вдруг провалился. Лезем в код — и вдруг находим что-то типа
return 12; // TODO: implement later 

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

7. Тестируем что-нибудь посложнее

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

8. Пишем тест на баг

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

9. Первый раз TDD

Как обычно выглядит ваша работа при разработке нового функционала? Наверное, вы сначала думаете. Потом проектируете то, что будете делать — набрасываете названия интерфейсов, классов, потом названия методов, наполняете их реализацией, запускаете, отлаживаете. Отлично, менять почти ничего не надо. Просто в тот момент, когда у вас уже есть интерфейсы, классы и названия методов, но еще нет их реализации — напишите для них тесты. Простенькие — вызвали метод — проверили результат. Обратите внимание, как уже на этом этапе вы заметите нелогичность некоторых имён, недостаток или излишество аргументов в методах, ненужные или отсутствующие зависимости и т.д.. При этом сейчас пока что это исправить — почти ничего не стоит (ведь реализация ещё не написана). Подправили архитектуру, дописали тесты, запустили — увидели кучу проваленных тестов. Отлично, так и должно быть. Написали реализацию, запустили тесты — увидели большинство из них пройденными, исправили ошибки, добились успешного прохождения всех тестов — отлично, дело сделано. Вы чувствуете, как хорошо стало, какое моральное удовлетворение вы получили? Оно слегка напоминает удовольствие от получения какой-то ачивки в игре. А почему? А потому, что его можно измерить! «Код проходит 18 тестов при тестовом покрытии в 90%» — это звучит круче, намного круче чем «ну, фича вроде бы реализована, я так потыкал немножко, кажется, не падает». Это даёт право гордится. Идешь домой — и чётко понимаешь, что-то за день сделал полезное, это «что-то» измеримо, ощутимо, реально.

10. Прикручиваем запуск тестов к CI-серверу

В тестах мало смысла, если их не запускать. Запускать их вручную — долго и бессмысленно. Наверняка у вас есть билд-сервер с каким-нибудь TeamCity или CruiseControl, где собирается ваш продукт. Так вот, большинство хороших билд-серверов сразу, из коробки, поддерживают запуск тестов и даже парсят их логи и рисуют красивые отчёты. Соответствие тут, конечно, не «все совместимы со всеми», но если вы взяли тестовый фреймворк по совету в начале статьи — шансы на то, что всё заработает очень высоки. К примеру, упомянутые мною TeamCity и Google Test прекрасно дружат между собой.

Послесловие

Дотошный читатель может заметить, что пункты начиная где-то с седьмого-восьмого скорее всего не впишутся в заявленные в заголовке «10 минут на шаг». Что тут можно сказать? Считайте, что я, нехороший человек, вас слегка наколол. Однако, если вы на практике с праведным негодованием прошли эти пункты, то:
  1. У вас уже есть проект, к которому прикручены тесты. Они запускаются, работают, их больше нуля и они уже приносят вам пользу.
  2. Вы получили опыт во всём этом деле.
  3. Во второй раз у вас получится серьёзно быстрее.

Вот и решайте, стоило оно того или нет.

Где-то пункта после 8-го — хорошее время чтобы представить тестовый проект вашей команде. Объясните в 2-3 абзаца что и как, покажите простенький пример теста, заметьте, что, мол, «feel free to add your own tests», но особо не напирайте пока. Если у вас писать тесты было не принято, скорее всего первым впечатлением будет осторожный скепсис и непонимание. Это быстро лечится после второго-третьего упоминания на совещании о том, что, мол «а этот баг мы нашли благодаря тесту» или «а вот тут написан тест и мы сразу узнаем, если оно сломается снова». Программисты — народ рациональный, они поймут и подтянутся.
Инфопульс Украина 223,83
Creating Value, Delivering Excellence
Поделиться публикацией
Похожие публикации
Комментарии 63
  • +6
    TDD в том виде, в котором его пропагандируют фанатики, не применим к реальному программированию. Покажите мне хоть один реальный большой проект на github, который был написан целиком при помощи этого подхода. Возьмем например eclipse, в команде которого долгое время работал основатель этого подхода, Кент Бек, покрытие тестами там далеко не 100%, и что-то мне подсказывает код там был написан не в виде TDD.

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

    Такой подход имеет морю преимуществ перед TDD,
    1. если код простой, то много тестов не нужно, и мы не тратим время на тестирование того кода, который может сломаться только чисто теоретически.
    2. если где-то возникают затыки и проблемы с качеством, мы легко можем увеличить покрытие и увеличить качество.
    Таким образом мы получаем реально agile подход, а не религиозную практику в проекте.

    Кстати, про тестирование, очень рекомендую вот эту книгу: www.amazon.com/Google-Tests-Software-James-Whittaker/dp/0321803027/ Она дает представление о том, как можно получать реальную пользу от тестов.
    • +2
      Такой сойдет?
      • Такой сойдет. Возникают следующие вопросы:
        — где там тесты?
        — каково тестовое покрытие?
        — сколько времени ходят тесты?
        • +3
          1. Папочки features и spec
          2. Около 85%
          3. Зависит от машины, на трэвисе около 18 минут, у меня на ноуте минут 20.
          • А вы уверены что все писали test first?

            У меня в текущем Java проекте, который я пишу используя обозначенный выше подход, покрытие критических подсистем ок 90%, не критически 60%, все тесты гоняются 3 секунды.
            • +1
              Я думаю большинство кода писалось именно в виде TDD. Но безусловно есть места где сначала писали код, а уже потом тесты.
          • 0
            2. Около 85%
            Чего? Функций, операторов, строк кода, всех возможных путей выполнения программы, всех возможных вариантов входных-выходных параметров?
            • +2
              Вот здесь можно посмотреть.
              • +1
                Это был риторический вопрос. Тонкий намек на то, что 85% вовсе не означает, что это хорошее покрытие.
      • 0
        Не уверен, что используется TDD, но тестов очень много:

        • Three independently developed test harnesses
        • 100% branch test coverage in an as-deployed configuration
        • Millions and millions of test cases
        • Out-of-memory tests
        • I/O error tests
        • Crash and power loss tests
        • Fuzz tests
        • Boundary value tests
        • Disabled optimization tests
        • Regression tests
        • Malformed database tests
        • Extensive use of assert() and run-time checks
        • Valgrind analysis
        • Signed-integer overflow checks

        www.sqlite.org/testing.html
        • +4
          Я не против тестов, я против догматичных практик, таких как TDD.
      • +2
        А у меня было с картинками :) habrahabr.ru/post/170847/
        Ваши шаги 5 и 10: сделать makefile или другой какой-нибудь скрипт, а потом еще заставить это работать на CI сервере с парсингом отчета дело далеко не для всех и не всегда на 10 минут. Поэтому, я в своей статье предпочел использовать NetBeans, который умеет делать такой makefile, точнее сказать, такой makefile у него является частью проекта. И показал настройку парсинга для конкретного CI сервера — Jenkins.

        А вообщем, все правильно. Если уже делать первые шаги к TDD, то конечно, надо начинать с тестирования HelloWorld.
        • +23
          GetAdressat

          По рукам надо бить за такое. Долой невалидный корявый английский.
          • –8
            Многим вероятно покажусь старомодным, но я никогда не писал тесты в том виде, в котором их сейчас преподносят.
            Я всегда обязательно тестирую систему, отлаживаю — а как же без этого? Но я не представляю, как можно написать тесты для сложной системы, в которой взаимодействуют множество модулей, есть работа веб-приложения, взаимодействие с пользователем, работа с БД. Протестировать все отдельно? Какой смысл тестировать систему на вами же придуманные ошибочные ситуации, да еще и отдельными маленькими кусочками. Система работает в совокупности, как единый механизм.

            И если уж ошибки возникают, то как правило совсем непредсказуемо, а не там, где вы их и так ожидаете, покрывая именно эти участки тестами.
            К тому же… любой тест — это тоже программная реализация, в которой так же могут быть ошибки. Будем писать еще тесты на тесты?
            Допустим, тест написан с ошибкой. И он проходит! Вы в полной уверенности, что код работает правильно, ищите ошибку в другом месте. Красота!

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

            Что-то подсказывает мне, что большинство разработчиков все же пишут, программируют как и раньше, но дабы не расстраивать приверженцев всякого рода TDD, потом уже пишут некоторые тесты для уже работающего кода.
            • +1
              > Но я не представляю, как можно написать тесты для сложной системы, в которой взаимодействуют множество модулей, есть работа веб-приложения, взаимодействие с пользователем, работа с БД.

              Помимо модульных тестов есть еще и интеграционные. И да, их тоже желательно автоматизировать, хотя это сложнее.
              • –1
                Я так-то в курсе. Немного интересовался этой темой. Но меня совершенно не впечатлило.
                Пишу системы без тестов и прекрасно себя чувствую! И да, пользователи моих систем тоже довольны надежностью их работы.
              • 0
                Тесты — это как двойная запись в бухгалтерии. Вроде как не нужно, но позволяет описать один и тот же кусок кода двумя разными способами, позволяя находить несоответствия и обязательные ошибки (наличие ошибок в программе — это аксиома). В тестах тоже могут быть баги, разумеется, но важен сам факт наличия несоответствий, он заставит вас обратить внимание на проблемное место (а оно обязательно будет либо в коде либо в тесте). Ну и абсолютный плюс при рефакторинге.
                • 0
                  Совершенно не согласен с сравнением с двойной записью в бухгалтерии. Не путайте просто цифры, суммы и алгоритмы.
                  Следуя вашим советам, самое верное будет — это написать 2 экземпляра одной и той же системы 2 разными способами, что не только не исключает возникновение ошибок, но увеличивает вероятность появления ошибок. Еще раз повторюсь — тест — это тоже программный код, алгоритм, который тоже запросто может содержать ошибку.

                  Насчет рефакторинга… В последнее время создается впечатление, что многие просто помешались на рефакторинге и покрытии тестами. Работает правильно — и пусть себе работает! Лучшее — враг хорошего. Пишите что-то новое, двигайтесь вперед!
                  • 0
                    Пишите что-то новое, двигайтесь вперед!

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

                      • 0
                        А сколько вас на проекте?
                        • –1
                          Все зависит от проекта. Участвовал в таких, где и 2 человека было, и 7.
                          А вообще предпочитаю заниматься индивидуальной разработкой. На данный момент сопровождаю несколько готовых проектов, реализованных мной, что не мешает мне писать новые.
                          • 0
                            Это я к тому, что пока на проекте один человек, все ок, все у него в голове, аритектура в его голове тоже идеальная, тесты не нужны.
                            Вы когда-нибудь занимались поддержкой чужого говнолегаси-кода над которым работало не одно поколение разработчиков?
                            • 0
                              Да, естественно, было и такое. Но там и тесты бы не помогли — уверен! Когда дело касается конкретного рефакторинга кода, то:
                              1. берется проектная документация, где описаны (не в коде) все алгоритмы, как оно должно на самом деле работать
                              2. смотрим в исходники и переписываем полностью весь неудачный код

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

                              я занимался оптимизацией и исправлением ошибок довольно большой системы… и, знаете, мне бы не пригодились тесты ни тогда, ни сейчас я не ощущаю в них потребности.
                • 0
                  А вы задумайтесь когда-нибудь над фактом, что если ваш тест стал сложным настолько, что в нем можно допустить ошибку, то ваш код плохо спроектирован. Это отличный показатель для качества кода. Иногда готовишь входные данные для вызова метода и понимаешь как же это неудобно тому, кто этот метод вызовет в реальной жизни… Но проще всего не обращать внимания на гниющую ногу…
                  • 0
                    Мне не нужно писать тесты, чтобы понять, хорошо или плохо спроектирован проект. Кстати, именно проект спроектирован, а не код, т.к. код как-то все же пишут. И никогда не сталкивался с подобной проблемой: «Иногда готовишь входные данные для вызова метода и понимаешь как же это неудобно тому, кто этот метод вызовет в реальной жизни…».
                    А вы задумайтесь над тем фактом, что любой тест — это программный код, что уже по определению предполагает возможность внесения в него ошибки, пусть даже это будет просто опечатка.
                    • +1
                      Уху! Наконец-то я нашел человека, который пишет идеальный код и сразу проектирует систему правильно! Теперь понятно, почему вам больше по душе работать в одиночку… [trollface]

                      Поэтому вам и предлагается работать по TDD: пишете тест -> убеждаетесь, что он не работает -> пишите код -> убеждаетесь, что тест заработал, не меняя теста. Еще ни разу в таком цикле не встречал, чтобы в тест закрадывалась проблема и после написания кода она исчезала. Обычно тест с ошибкой продолжает не работать и после написания кода, что демонстрирует ошибку в тесте. Ошибка исправляется и потом цикл повторяется. Таким образом код и тесты помогают валидировать друг друга.
                      • 0
                        Я не пишу идеальный код, ибо никто на это пока не способен, но проектированию уделяю достаточно много времени перед началом разработки. Бывает и так, что чего-то сразу не учел в проекте, но, как правило, это уже дополнительные хотелки заказчика, которые вряд ли можно было сразу предугадать. Но и в тех случаях я достаточно легко изменяю функционал, чтобы он соответствовал требованиям заказчика.

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

                        Очень хорошо зарекомендовала себя следующая практика:
                        1. Проектируем работу алгоритма, если нужно, разбиваем его на некие составляющие.
                        2. Кодируем алгоритм
                        3. Проверяем правильность его работы
                        4. Исправляем ошибки, отлаживаем. п.3
                        5. Готово

                        На кой черт мне еще писать тест, чтобы еще и он правильно заработал?

                        Или Вы предлагаете опровергнуть хороший проект с хорошо комментированным, протестированным и отлаженным кодом с помощью избыточного программного кода тестов?
                        • +1
                          А если не трудно, можете раскрыть подробнее вот этот пункт:
                          > 3. Проверяем правильность его работы

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

                              Для того, чтобы вызвать методы класса Вы написали какой-то код, и передали методам какие-то параметры. Затем посмотрели что эти методы сделали. А затем сравнили результат с тем, что Вы ожидали. То есть, убедились в том, что они сделали именно то, что Вам нужно. Откуда Вы взяли эти данные? Есть несколько вариантов.

                              Вариант 1. Вы сразу встроили объект(ы) Вашего класса туда, где они будут использоваться и для ввода данных Ваших методов их нужно ввчести вручную с клавиатуры/мышки и т.д. Этот вариант прост и быстр, когда проверяемый Вами класс находится относительно близко от UI (каким-бы оно не было, графическим, веб или консольным). А вот если для передачи данных от пользователя требуется передать их через несколько уровней абстракции, то может их передать не очень просто.

                              Вариант 2. Вы просто захардкодили входные данные где-то в том месте, где Вы его собираетесь использовать, просто чтобы не вводить их каждый раз после очередного исправления и не передавать через несколько слоев абстракций.

                              Вариант 3. Вы написали отдельную маленькую программку, которая использует свежесозданный Вами класс. (отдельный main, если, напрмиер, на C/C++). И в этой программке Вы уже погоняли все методы Вашего класса для всех возможных, а может быть и невозможных данных.

                              Быстрее и проще всего, конечно, 1-й вариант. Но если Ваш класс сложен и Вы не написали его сразу безошибочно, то скорее всего Вам придется вводить несколько разных наборов данных, чтобы убедиться, что Ваш класс работает корректно при разных входных данных или другом начальном состоянии объекта. Вводить эти данные вручную каждый раз, при каждом прогоне не очень веселое занятие… Вы ведь можете и с вводом данных ошибиться. Поэтому, лучше вариант 2. Но у него есть свои проблемы — можно потом (после того как Вы закончите отладку нового класса) забыть вычистить отладочный код. Или позже может выясниться, что нужно еще на каких-то наборах данных проверять.

                              Поэтому, хорошо бы вариант 3… Но Вариант 3 такой муторный!!! Это надо отдельный проект или отдельную конфигурацию делать. И куда потом этот main деть? А еще его выбрасывать жалко. Вы же его написали. Время потратили. Пусть хранится.

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

                              И такой механизм существует…
                              • 0
                                Я предполагаю, что Вы сейчас ожидаете примерно следующего ответа на свою ключевую фразу «И такой механизм существует… »:

                                [не вставилось фото]
                                вот ссылка: inet777.ru/images/o_rly.jpg

                                И что это за чудесный механизм?

                                Но разочарую Вас.
                                Я использую примерно смесь п.1 и п.2. Хотя возможны различные варианты на тему отладки.
                                А вот если класс меняется или меняются какие-то условия входных данных, то я точно так же протестирую только тот функционал, который подвергся изменениям или требует проверки для новых условий.

                                Не вижу смысла писать программы для проверки корректности программ же. Я предпочитаю все же проектирование системы, а если это действительно сложная система с множеством взаимосвязей, то и проект будет точно так же не простой.
                          • 0
                            Жесть, пройдет несколько лет и перечитайте этот пост. Больше я вам ничего не скажу.
                            • 0
                              Т.е. Вы полагаете, что я — начинающий программист без достаточного опыта? Ну ну…
                              Я бы Вам посоветовал быть более осторожным с подобными выводами. Больше наверное тоже ничего я Вам не скажу.
                  • +1
                    Я вот за собой заметил, что как только мой код покрыт тестами — я начинаю пороть в нем херню. Изменения не обдумываю, за control flow не слежу, намного активнее пробую разные подходы и меньше подвергаю их критической оценке.

                    Провалился тест с кавычкой в исходных данных — а давай засунем addslashes() куда-нибудь. Засунули — оп, упало еще 50 тестов. Значит не сюда, значит в другое место попробуем. Или через preg_replace('/\'/' может лучше? И так пока все не загорится зелененьким.

                    Я не задумываюсь о последствиях preg_replace в данном конкретном месте, просто тупо пробую. Какая разница, ведь мой код покрыт тестами? Если что-то неправильно — я всегда это увижу!

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

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

                    И я виню в произошедшем именно TDD. Действительно стало легче писать код. Намного легче. Ведь это именно тесты позволяют мне не думать. А я — ленивая тупая скотина, и как только стало возможным не держать все в голове, я с радостью воспользовался этой возможностью. Такие дела.
                    • +2
                      Ну да, в том, что люди меньше читают, виноват телевизор. В том, что не катаются на велосипедах — автомобили. В том, что редко общаются вживую — эти ваши интернеты. Правительство там еще можно прикрутить куда-нибудь. Ленивая тупая скотина (прямая цитата автора) всегда найдет, на кого можно спихнуть свою лень и тупость.
                      • 0
                        Именно так и есть! Если можно чего-то не делать, то большинство и не будет этого делать. Вы очень верно привели в пример телевизор, автомобиль, интернет… эти блага прогресса одновременно и ограничивают большинство из нас, потакая нашей человеческой сущности — лени. Это не причина чего-то не делать, но это огромный соблазн в сторону ничегонеделания, согласитесь.

                        Не нужно приписывать человеку те человеческие качества, которые он приобретает за счет усиленной рекламы образа жизни, поведения, рекламы всех новшеств, прогресса. Мы — социальны — и это для многих определяет стиль их жизни — быть как большинство. На что большинство ведется? Если ты не в ногу с прогрессом, значит, ты — лох! Ездишь на велике, а не на собственном авто — лох! Не пользуешься социальными сетями — фууу!.. Человека без мобильника вообще уже как неадеквата воспринимают. Ну и, естественно, не пользуешься тестами при программировании — минус тебе, минус и еще раз минус!.. Будь как большинство — используй тесты — только тогда ты сможешь считаться по-настоящему программистом! Как-то так…

                        Но суть в том, что (зачастую мнимые) подарки прогресса не всегда несут только благо человеку — попутно они нас людей и расслабляют, отупляют. Вот вам и пример результата использования очередного нововведения для облегчения труда программиста. И, знаете, я думаю, что «fogx» прав! И он это осознает, и может сказать об этом, а большинство только восхищаются, не осознавая при этом, что полагаются уже во многом на тесты, а не на собственную голову и память.
                        • 0
                          Нужно отдавать себе отчет в том, что ты делаешь. Не тесты делают твой код плохим — ты его делаешь плохим. Тот же самый говнокодище можно писать и с тестами, и без тестов, и на руби, и на пыхе. Мозги никто не отменял, и, как я уже писал ранее, тесты мозги не заменяют. Многие воспринимают тесты как панацею от всего, многие — как псевдонауку и трату времени. Но они — не то и не другое. Это подспорье, очень хорошее подспорье. Самопроверяющаяся документация к проекту. По хорошо написанному тесту (даже по его заголовкам) можно понять назначение и принцип работы функции или класса. Тесты помогают безопасно проводить рефакторинг кода. Но тесты никоим образом не вынуждают писать говнокод. Не зря в цикле TDD три этапа, а не два. После получения зеленых тестов обязательно нужно провести рефакторинг нового кода. Но не методом «дай ка я вставлю эту функцию тут», а вдумчиво.

                          Не тесты делают код плохим, код становится плохим, когда кто-то решает, что теперь можно его плохо писать, потому что теперь есть тесты.

                          Говоря о человеческой природе… Я езжу на другой конец города в секцию боевых исскуств (велик — не мое, к сожалению), читаю книги, не имею дома телевизора, периодически встречаюсь с друзьями. А еще я пишу хороший код, пользуя BDD. Такие дела.
                          • 0
                            Давайте по порядку тогда.

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

                            2. Я все же предпочту хорошо комментированный и хорошо документированный код, а не код с тестами, которые якобы еще и документацией должны отчасти служить.

                            3. Что за мания делать рефакторинг при любом удобном случае? Тоже дань моде, подкрепленная уверенностью в тестах, что ничего не сломается? Быть может все же стоит относиться более ответственно к проектированию системы и написанию чистого кода с хорошими комментариями изначально, а? Чтобы не было вот такого: «После получения зеленых тестов обязательно нужно провести рефакторинг нового кода.»

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

                            О человеческой природе…
                            А кто Вам сказал, что Вы пишите хороший код? Сами так решили? Похвально! А без тестов пробовали? Ах да, сломается же все быстро… как же хорошему программеру без знатного рефакторинга, как-то не по-программерски, понимаю. А чего телек не купите? Денег не заработали своим хорошим кодом? Так, для разнообразия, пусть стоит как мебель… новости хотя бы посмотреть. Хотя да, сейчас все есть в интернет, согласен. Сам телек не смотрю, но он у меня есть. Чтение книг — полезно, тут респект! С друзьями практически все периодически встречаются, но все отличие в том, что когда не было инета, то встречались практически каждый день.
                            Ничего сверхординарного и удивительного — обычный среднестатистический обыватель с отклонениями от общепринятого поведения в пределах нормы. Видимо, несколько разочарую вас, но чего-то уж очень уникального и из ряда вон я не заметил.

                            Собственно, о тестах. Я лишь хочу сказать, что какой-то уж очень значительной выгоды и облегчения труда программисту они не приносят. Нравится Вам — ради Бога, пишите избыточный код, если это добавляет Вам уверенности в надежности Вашей системы. Только не нужно преподносить тесты как нечто совершенно необходимое при разработке систем, программ. Без них пишутся многие системы и прекрасно работают.
                            • 0
                              1. Еще раз — это он почему-то решил, что теперь можно писать плохо. Тесты тут не виноваты.
                              2. Одно другому не мешает. Но ваши комменты к коду не скажут вам, что то-то пошло не так.
                              3. Это не мания, это принцип TDD. Пишем тест, пишем максимально быстро код для этого теста, делаем этот код красивым. Это правда работает, поверьте.
                              4. Хорошо написанные тесты дают далеко не мнимую уверенность.

                              Без тестов пробовал. С тестами удобнее.
                              Телевизор как мебель есть, телевидения нет. Не нужно.
                              Я и не говорил, что я уникален.

                              Вы можете не писать тесты, ради бога. Можете даже код не комментировать — еще время сэкономите. И не тестировать руками. Еще немного экономии. Попрыгали по клавиатуре — и в продакшн. Зато быстро же.
                              • 0
                                1. Я все-таки полагаю, что он не одинок в этом. Тесты — косвенная причина такого поведения, о чем указал сам автор.

                                2. Тогда не стоит путать… пусть дока остается докой, а тесты — тестами — только сигнализаторами об ошибках.

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

                                4. Останусь при своей точке зрения.

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

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

                                Позволю себе некоторые цитаты:

                                «Требуется больше времени на разработку и поддержку, а одобрение со стороны руководства очень важно. Если у организации нет уверенности в том, что разработка через тестирование улучшит качество продукта, то время, потраченное на написание тестов, может рассматриваться как потраченное впустую.»

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

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

                                «Большое количество используемых тестов могут создать ложное ощущение надежности, приводящее к меньшему количеству действий по контролю качества.»
                                • 0
                                  Я просто оставлю это здесь habrahabr.ru/post/171883/
                                  • 0
                                    В качестве добавки: далеко не всегда приходится переписывать свой код. Часто это чужой код в миллионы строк. Запущенный один раз тест выдаст на экран всю документацию по проекту. Могут ли так сделать комменты?
                                    • –1
                                      миллионы строк чужого кода — Линукс — не менее того!
                                      Линус Торвальдс? Ой, простите, не узнал Вас под этим ником на хабре…

                                      Да Вы просто Бог! По сообщениям тестов понять проект в миллионЫ строк, не читая при этом в документацию и комментарии — это гениально! Бурные аплодисменты, переходящие в авации и сопровождаемые восторженными криками!

                                      «далеко не всегда приходится переписывать свой код.» != «Пишем тест, пишем максимально быстро код для этого теста, делаем этот код красивым.»

                                      уже коллизии в комментах появляются… пора писать тесты перед каждым сообщением! ;)
                                      • 0
                                        Хм, ок, аргументы у вас кончились, начался тупой троллинг.
                                        • +1
                                          Да нет, Уважаемый, это Вы уже начинаете путаться в собственных мыслях.

                                          Аргументы свои я достаточно четко уже изложил.

                                          Конечно проще оскорбить человека, сравнив его с приматом, прыгающим на клавиатуре, назвать троллем, тогда как сам не в состоянии внятно и разумно объяснить и доказать собственную правоту. Затем вознести себя до небес миллионами строк чужого кода и «А еще я пишу хороший код, пользуя BDD.». Такой ход давно известен, называется «Сам дурак!».

                                          На сим, я полагаю, наш диалог можно считать завершенным. Спасибо.
                                        • 0
                                          1 миллион строк исходного кода это вполне реальный проект, который разрабатывается и поддерживается командой из примерно 10 человек, в течение, примерно 10 лет.

                                          Таких проектов много. Очень много. Я принимал участие в двух таких проектах за последние 6 лет. Еще один проект перевалил 1млн строк уже после того, как я оттуда ушел.

                                          Ядро Linux перевалило этот объем в районе 2000-го года. Сейчас более 15млн строк.

                                          Конечно, если Вы работаете один, то у Вас не будет проектов в 1 млн строк исходного текста. И може быть Вам, на самом деле не надо тестов.
                                          • 0
                                            А можно поинтересоваться спецификой тех проектов на миллионы строк кода, которыми Вы занимались и конкретно Вашу реализацию в данных проектах? Еще было бы очень занятно узнать, сколько из этих миллионов строк занимают тесты?
                                            • 0
                                              Про один проект рассказать могу:
                                              — сервер, выполняющий несколько сложных задач: система версионного контроля документов, issue tracker, система сборки, кое-что еще по мелочам.
                                              — десктопный клиент к нему (точнее сказать, несколько клиентов для разных задач — использование и администрирование)
                                              — библиотеки интеграции к несколькими сторонними десктопными клиентами.
                                              — веб сервер, предоставляющий веб интерфейс к этому серверу, который используется, в том числе и интеграциями к сторонними клиентами из предыдущего пункта.

                                              Когда я в нем участвовало проекту было уже 13 лет. 3.5 млн строк итого, 1.8 млн строк на Java.

                                              Моя роль — разработка. В разных частях проекта. В том числе и тесты.

                                              Тесты там были только интеграционные. И занимали около 10% исходного кода по строкам. Тестов было мало, потому что их созданием озаботились только после, примерно, 8 лет существования проекта, когда стали понимать, что поддерживать функциональность без тестов уже невозможно — ручное тестирование слишком дорого. Функциональности много. Ну и как в это время тесты стали популярны.
                                • 0
                                  Тю, да конечно я делаю свой код плохим, кто ж еще? И я вполне отдаю себе в этом отчет. И также понятно, почему именно я это делаю — я почувствовал безнаказанность.

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

                                  Конечно, никто не мешает и сейчас программировать вдумчиво и осторожно, а ездить только на велосипеде. Просто тесты (и автомобили) сделали это необязательным, и тем здорово упростили жизнь (лично мне).
                                  • 0
                                    Хотя раньше тратил в сто раз больше усилий мозга, сидел в дебаггере, перепроверял себя по десять раз и все равно имел шанс где-то что-то забыть


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

                                    Тесты НЕ делают красивый код необязательным. Вообще не понимаю, откуда эта чушь взялась в ваших головах.
                                    • 0
                                      Да нет же, как раз делают. Раньше красивый код был необходимостью — иначе я бы сам не смог в нем ориентироваться и тогда уж точно наделал бы ошибок. Да и «перепроверка себя по сто раз» в случае запутанного, непонятного кода — вряд ли осуществима.

                                      А сейчас я могу проверять любой код. Для того, чтобы убедиться, что код не содержит ошибок, мне больше не нужно на него смотреть и анализировать. Я могу, конечно, это делать, но могу теперь и не делать. Раньше не мог не делать, теперь могу. И даже злоупотребляю этим.
                                      • 0
                                        Если вы поставите пожарную сигнализацию, начнете ли вы менее бережно обращаться с огнем? Будете ли менее тщательно проектировать проводку? Если поставите сигнализацию на машину или квартиру, станете ли оставлять ее открытой? Дико сомневаюсь. Ваше поведение останется прежним, просто вы будете спать крепче по ночам. Так и с тестами: ничего не поменялось, просто появился парень, прикрывающий вам спину и сводящий ваши ошибки к минимуму. Я так и не понял, с какого перепугу я вдруг должен начать писать код, за который мне будет стыдно.
                                        • 0
                                          У вас просто дар какой-то приводить некорректные примеры. Ошибка в программе — это далеко не уничтоженная навсегда собственность пожаром.
                                          И, кстати, да… и чего это вдруг столько машин во дворах развелось по ночам? Раньше как-то в гараж старались спрятать. Должно быть, тут все же сигнализация успокаивает владельцев авто. Не так ли? А за это спокойствие многие автовладельцы и платятся вещами, забытыми в авто, автомагнитолами и колесами. Вот это более явная аналогия.
                                          • 0
                                            Я так и не понял, с какого перепугу я вдруг должен начать писать код, за который мне будет стыдно.

                                            Постараюсь объяснить еще раз: 1) потому что так проще и 2) это более не приводит к ошибкам. Раньше писать тяп-ляп у меня не получалось, я так или иначе должен был садиться и вникать. Теперь я «сажусь и вникаю» только если мне этого вдруг захочется, или появится куча лишнего времени. Если же времени нет, и я очень спешу, то «парень, прикрывающий мне спину» оказывается вполне ощутимым подспорьем, позволяет мне кодить не думая и сам следит.за тем, чтобы я нигде не напортачил.

                                            Почему я отключаю мозг, едва появилась возможность это сделать? Потому что я ленивый. (и тупой, уже писал выше). Вы, очевидно, другой, и продолжаете писать вдумчиво даже когда это не необходимо. Не ищете легких путей или я не знаю что.
                                            • 0
                                              Я пишу код красиво для того, чтобы мне его было проще поддерживать. Тесты, собственно, пишутся с той же целью. Я никогда в жизни не поверю в код, который никогда не будет правиться. Если оный существует, то это код мертвого проекта. Так вот, если я напишу плохой код, я истрачу кучу времени, сил и нервов на его поддержку. И это не зависит от наличия тестов. Я просто уважаю себя и того парня, что будет поддерживать написанное мной.

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

                                                Да я бы вообще в остальные дни перестал писать! Зато по четвергам выкладывался бы по максимуму — стараясь успеть как можно больше по количеству, но не по качеству (ведь за качество отвечает Добрый парень).

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

                                                Может быть, я бы ее не гнал, если б был более сознательным, но тогда успевал бы в четверг значительно меньше. И уж точно я бы её не гнал, если б она не прокатывала. А так — имеем, что имеем. Я стараюсь успеть побольше, Добрый парень мне всё прощает.
                                                • 0
                                                  Т.е. в тестах вы нашли того, кто говорит вам «Все зашибись, бро, твой код прекрасен»? Ну так вы его не так поняли, он сказал, что ваш код работает, он не следит за его красотой. Это делают другие парни. Code Climate, например.
                            • 0
                              не туда
                              • 0
                                Вопрос к специалистам по TDD.
                                Регулярно читаю о том какой TDD хороший, и мучаюсь от такого. что никак не могу его применить. Я разработчик SMS-шлюза. Типичная messaging система: принял сообщения от внешней системы, чего-нить с ним поделал, отправил дальше и так во все стороны. Основная часть работы системы это: сетевой обмен, отправить/получить из БД (или очереди), переформатировать, маршрутизация. Естественно, при этом возникает куча процессов.

                                Ну вот никак не понимаю как тут применять TDD. Почитал wiki получается, что многопроцессные messaging системы — это сплошные слабые места для TDD:
                                * сетевой обмен
                                * БД
                                * межпроцессное взаимодействие

                                Привлекает в TDD то, что внеся любые изменения (а их вносить приходится много и постоянно), можно вжих-х-х — прогнать тесты и найти проблемы.

                                Подскажите куда капать, чего почитать? Или это не лечится?
                                • 0
                                  * Сетевой обмен. Если нет возможности поднять локальный сервер для тестов или организовать какой-нибудь sandbox, то обмен по сети обычно эмулируется. Если сервис сторонний, то за правильностью его работы следят его разработчики, это уже не ваша забота, вы проверяете правильность отправки данных на сервер и правильность обработки ожидаемых вернувшихся данных. Если в спецификации к сервису описаны возможные коды ошибок, отказов, итд — эмулируете, проверяете. Если же это ваш сервис, что он проверяется отдельно на прием данных извне, их обработку и отправку во внешний мир. Таким образом разделяется поле ответственности.
                                  Для тестирования сети в Ruby, например, существует уйма библиотек. Моя любимая — WebMock.

                                  * БД. А какие проблемы с БД? Создаете тестовую базу данных, наполняете тестовыми данными — и вперед.

                                  * Межпроцессоное взаимодействие. Почти аналогично сетевому обмену: эмулируете интерфейс взаимодействия с другим процессом, проверяете правильность работы вашего кода с ним. Правильность работы того_самого_процесса должен проверять его разработчик.

                                  Я, например, периодически эмулирую даже обращение кода к Redis. Если команда простая (get/set), то необычного поведения системы не предвидится. Причем, эмуляция дает нам пару плюсов: 1) код не будет обращаться к внешнему процессу, что сэкономит время; 2) тест сообщит нам, если код по какой-то причине лишний раз обратится к проверяемой функции или передаст не те аргументы.

                                  Безусловно, есть те вещи, которые слишком сложно или невозможно покрыть тестами. Тут уж ничего не поделаешь. TDD — это не г-подь б-г, а всего-навсего инструмент, причем, не везде применимый.

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

                                Самое читаемое