Рубрики
Без рубрики

Пошаговое введение в модульное тестирование на Java

Оглавление Оглавление Введение Наивный способ тестирования кода Что такое Единица измерения… С пометкой “Новички”, “тестирование”, “java”.

содержание

  • содержание
  • Вступление
  • Наивный способ тестирования кода
  • Что такое модульное тестирование?
    • Аналогия
  • Промокаешь ноги
    • Предпосылки
    • Образец существующего кода
    • Настройка платформы тестирования
    • Разделение исходного кода и тестового кода
    • Написание вашего первого модульного теста
    • Запуск теста и использование результатов для отладки
  • Заключительные примечания
  • Дальнейшее Чтение

Вступление

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

В любом случае, хватит моей болтовни. Давайте сразу перейдем к делу.

Наивный способ тестирования кода

Когда мы только начинали изучать программирование, то обычно писали код и запускали программу. Затем проведите тесты, введя некоторые входные данные, надеясь, что результат будет таким, как мы хотели. Если нет, мы снова возвращаемся к написанию нашего кода и исправлению ошибок.

Что ж, в этом нет ничего плохого. На самом деле, всю свою жизнь в качестве программиста вы бы занимались всеми этими тремя вещами:

1) написать код

2) выполнить программа

3) протестируйте поведение вашей программы

Затем повторите еще раз

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

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

Что такое модульное тестирование?

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

Аналогия

Например, вы строите дом, и вам нужно несколько лампочек. Итак, вы идете в магазин и покупаете лампочку. Чтобы убедиться, что лампочка, которую вы только что купили, работает, вам нужно ее протестировать.

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

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

Промокаешь ноги

Предпосылки

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

Также стоит отметить, что руководство на данный момент также написано для пользователей macOS или Linux. Но опытные пользователи Windows также могут захотеть попробовать в любом случае (и дайте мне знать, где это становится трудным! Я буду вносить обновления в это руководство, где это необходимо).

Ниже приведен краткий список всего, что вам нужно:

  • Java 8 или выше (я использую Java 8. Но чтобы следовать этому руководству, не имеет значения, используете ли вы более новую версию)
  • Любимый Редактор кода или IDE (Я сам использую IntelliJ IDEA, но вы вольны использовать все, что захотите)
  • Gradle инструмент сборки (скачайте и следуйте руководству здесь )

    • в случае, если вы уже используете Gradle, вы можете перейти к этому шагу и просто добавить JUnit в свои зависимости
  • Командная строка (оболочка или командная строка)
    • мы будем делать все с нуля, так что знакомство с работой в терминале – большой плюс
    • это также делает это руководство независимым от IDE, что означает, что вам не нужно сильно зависеть от конкретной IDE, чтобы заставить это работать
    • если вы используете Windows, вы можете выбрать использование WSI или командной строки, которая поставляется с Git (если вы уже используете ее).

Образец существующего кода

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

Таким образом, мы можем реализовать это, написав следующее:

Возможно, вы заметили здесь “ошибку”. Это сделано намеренно. Мы углубимся в это позже

На этом этапе структура вашего проекта должна выглядеть примерно так. Ничего страшного, если у вас нет папки .idea или файла * .iml . Это всего лишь файлы, сгенерированные IntelliJ IDEA.

Настройка платформы тестирования

Платформа тестирования, которую мы собираемся использовать, – это JUnit . Чтобы добавить его в наш проект, нам нужен менеджер зависимостей , такой как Gradle.

💡 Менеджер зависимостей Самое замечательное в ООП то, что оно позволяет нам повторно использовать чужой код. Эти повторно используемые фрагменты кода часто достаточно зрелые, чтобы они могли быть автономными библиотеками или фреймворками. Затем они распространяются различными способами, либо загружая файлы JAR, как в случае Java, либо получая исходный код и создавая его самостоятельно. Эти библиотеки или фреймворки становятся зависимостями вашего проекта. Некоторые из этих библиотек/фреймворков имеют собственные зависимости, поэтому вы также должны позаботиться о них и добавить в свой проект.

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

/| менеджер зависимостей – это инструмент, который помогает вам загружать библиотеки и/или фреймворки, а также их зависимости для добавления в ваш проект; при этом также отслеживается версия, которую вы используете для каждого из них. Вот некоторые примеры популярных менеджеров зависимостей для Java-проектов: Gradle и Maven (который также функционирует как инструменты сборки).

Для получения дополнительной информации о менеджерах зависимостей и о том, что они могут сделать, ознакомьтесь с этой статьей Сеуна Мэтта в Medium

Если вы подготовили предварительные условия, перечисленные ранее, в вашей системе уже должен быть установлен Gradle. Чтобы проверить, введите следующее в свой терминал, и он должен вывести каталог, в котором вы установили Gradle.

$ which gradle

Далее нам нужно превратить наш существующий проект консольного приложения в проект Gradle. Разверните каждый шаг, щелкнув раскрывающийся список, затем следуйте инструкциям

  • Перейдите в каталог вашего проекта

    В моем случае мой проект сохраняется в Users/gerv/Source/Hello Unit Testing . Я могу использовать ~ в качестве сокращения для моего домашнего каталога пользователя.

  • Инициализировать проект Gradle

    Просто введите следующую команду и следуйте инструкциям на экране, чтобы настроить gradle для вашего проекта

    Если вы в замешательстве, вы можете просто следовать приведенному ниже скриншоту.

    Вы можете заметить, что мое приглашение командной строки отличается. Это потому, что я использую zsh с oh-my-zsh, но это тема для другого дня. А пока думайте об этой причудливой стрелке как о знаке $ , который вы обычно видите.

  • Запустите свою первую сборку Gradle

    Затем вы должны увидеть результат, подобный этому:

Вы можете заметить, что в папку вашего проекта добавлена куча файлов. Это файлы, которые генерируются gradle init и будут использоваться Gradle при создании вашего проекта. Вы можете с радостью игнорировать их сейчас, но я хочу быстро познакомить вас с одним из них, файлом build.gradle . Это файл, содержащий список ваших зависимостей и репозиториев (из которых будут загружены ваши зависимости). Взгляните на него и обратите внимание, что JUnit уже добавлен; это потому, что мы выбрали JUnit ранее, когда запускали gradle init .

Вы также можете заметить, что в проект добавлены папки main и test . Мы будем использовать их для реорганизации нашего кода.

Разделение исходного кода и тестового кода

Gradle недостаточно умен, чтобы знать, что у вас уже есть существующий код в вашем проекте, и предполагает, что вы начинаете проект Gradle с нуля. Таким образом, он создает свои собственные папки, пакет, класс с именем App.java с помощью метода main() .

После запуска gradle init каталог вашего проекта должен выглядеть следующим образом.

Но у нас уже есть Консольное приложение.java и это служит основной точкой входа вашего приложения, так что мы можем просто избавиться от App.java . Нам также не нужен пакет Hello Unit Testing , так как у нас уже есть существующий ранее. Удалите их, как вы обычно делаете, или если вы похожи на меня и вам нравится все делать в терминале

$ rm -r src/main/java/HelloUnitTesting
$ rm -r src/test/java/HelloUnitTesting

Затем нам нужно реорганизовать наш код и поместить их в папку main/java/ . Чтобы сделать это, вы можете либо перетащить com.gev.guev (или каково бы ни было ваше существующее имя пакета) в main/java/ или сделайте это снова через терминал

$ mv src/com src/main/java/com

Если вы также используете IntelliJ IDEA, вы можете заметить, что main/java помечен как пакет, хотя это не так. Чтобы они выглядели как обычные папки, просто щелкните правой кнопкой мыши на src , затем укажите на Отметить каталог Как и выбрать Снимите пометку как Корневой источник . Затем щелкните правой кнопкой мыши на папке java в разделе main затем Отметьте каталог Как и выберите Sources Root. Проделайте то же самое с папкой java в разделе test , но выберите Test Sources Root . Для папки resources пометьте их как Resources Root и Test Resources Root для main и тест соответственно.

Если вы внимательно следовали инструкциям, ваш каталог проекта теперь должен выглядеть примерно так:

Чтобы быстро проверить, все ли по-прежнему работает, запустите это, и оно должно сообщить вам “Сборка выполнена успешно”. Вы также можете попробовать запустить свою программу, если она все еще работает по-прежнему.

$ gradle build

Если ваша сборка gradle прошла успешно, но ваша среда разработки жалуется (т.Е. волнистые красные линии), вы можете просто повторно импортировать/повторно открыть свой проект, и, надеюсь, ваша среда разработки распознает, что теперь это проект Gradle.

Краткий обзор! Это многое из того, что мы уже рассмотрели. Вот что мы сделали до сих пор:

  • превратите наш проект в проект Gradle
  • добавьте JUnit в наши зависимости
  • Реорганизуйте наши папки проектов

Написание вашего первого модульного теста

Теперь, когда мы закончили настройку вашего проекта и его зависимостей, мы готовы написать наш модульный тест!

В папке test/java создайте пакет com.gev.guev или любое другое имя пакета, которое вы уже использовали в своем проекте. Затем создайте файл с именем Затем создайте файл с именем

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

В приведенном выше коде @Test – это аннотация, которая сообщает нашему компилятору, что метод, который мы только что написали, является методом test/| . @Test аннотация находится под пакетом org.junit (обратите внимание на инструкцию import выше).

Метод, помеченный как test method , будет проверен тестировщиком, если он удовлетворяет некоторым ожидаемым нами условиям. В этом случае тест всегда должен проходить, потому что мы указали ему значение assert true , которое всегда будет истинным, несмотря ни на что. Смысл написания этого в первый раз состоит в том, чтобы проверить, действительно ли наша платформа модульного тестирования была настроена правильно. После того, как вы написали один из них или если вы уже знакомы с используемой вами платформой тестирования, нет необходимости писать его каждый раз.

Чтобы запустить тест, введите эту команду

$ gradle test --tests="com.gerv.guev.MyStringUtilitiesTest.someUselessTest"

В приведенной выше команде мы говорим Gradle запустить тест, найденный с полным именем. В этом случае мы говорим ему специально запустить какой-нибудь бесполезный метод Test() test.

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

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

$ gradle test --tests="com.gerv.guev.MyStringUtilitiesTest.*"

Запуск теста и использование результатов для отладки

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

Продолжайте и запустите тест, используя команду, упомянутую ранее, и вы должны увидеть ошибку, подобную этой

> Task :test FAILED

com.gerv.guev.MyStringUtilitiesTest > shouldReturn_ReverseString FAILED
    org.junit.ComparisonFailure at MyStringUtilitiesTest.java:14

1 test completed, 1 failed

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/gerv/Source/HelloUnitTesting/build/reports/tests/test/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
3 actionable tasks: 1 executed, 2 up-to-date

Это говорит нам о том, что наш тест провалился. У Gradle есть довольно аккуратный способ сообщить нам об этом и сгенерировать отчет в формате HTML. Скопируйте этот путь к файлу и откройте его с помощью своего браузера.

Отсюда написано: org.junit. Ошибка сравнения: ожидалось:<[olleh]> но было:<[????o]> в первой строке трассировки стека. Это говорит нам о многом:

  1. наша входная строка не была перевернута, следовательно, она не равна нашему ожидаемому результату “olleh”
  2. фактический результат не содержит правильных символов или, вероятно, пуст. Вместо этого он отображает несколько вопросительных знаков.
  3. Фактический результат имеет ту же длину, что и наш ожидаемый результат. В нем 5 символов.
  4. Последний символ “o” по-прежнему является последним символом в фактическом выводе

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

В нашем примере он гласит: org.junit. Ошибка сравнения: ожидалось:<[olleh]> но было:<[????o]>

затем я пропускаю строки, пока не увижу знакомое имя пакета: в com.gev.guev. Мой stringutilitiestest.shouldReturn_ReverseString(Мой stringutilitiestest.java:14)

Это говорит нам о том, что в строке 14 нашего класса My StringUtilitiesTest произошла ошибка ComparisonFailure .

Учитывая эти результаты, мы можем проверить наш код, чтобы увидеть, что было не так. Вот текущий код с комментариями. Можете ли вы определить, что не так?

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

Теперь запустите свой тест еще раз, и он должен дать этот приятный успех

gradle test --tests="com.gerv.guev.MyStringUtilitiesTest.*"

BUILD SUCCESSFUL in 1s
3 actionable tasks: 2 executed, 1 up-to-date

Предположим, мы собираемся добавить функции в наш класс stringutilities, такие как обнаружение палиндрома

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

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

Заключительные примечания

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

Дальнейшее Чтение

  • У Parasoft есть краткое руководство по настройке JUnit, и оно даже учит, как использовать его без инструментов сборки, таких как Gradle или Maven. Иди и проверь это здесь
  • Существуют и другие отличные платформы тестирования, доступные для Java, такие как Testing и Спок . Лично я предпочитаю Spock, а не JUnit из-за некоторых функций и синтаксических сахаров. Предостережение в том, что оно написано с использованием Groovy который является динамически типизированным языком. Новичкам может быть трудно быстро понять это в дополнение к пониманию модульного тестирования.
  • Для .NET пользователи, есть xUnit в качестве фактической основы тестирования для .СЕТЕВЫЕ приложения. Это (возможно) преемник более старого NUnit . Для других языков просто попробуйте добавить первые несколько букв любого языка, который вы используете, а затем “-Unit”. Например, Junit, PHPUnit, PyUnit и т.д.
  • Некоторые методы разработки программного обеспечения, такие как Разработка на основе тестирования (TDD) и разработка, ориентированная на поведение (BDD), основаны на мастерстве модульного тестирования. Эти методы помогут вам сознательно разрабатывать функции, сохраняя при этом надежность вашей системы с течением времени.
  • Если вы программист среднего или продвинутого уровня, я настоятельно рекомендую вам прочитать статьи Мартина Фаулера о Модульном тестировании , других уровнях тестирования и концепция самотестирующегося кода
  • Модульное тестирование также может помочь вам абстрагироваться от слоев вашего приложения с помощью двойных тестов. Вы можете прочитать другую статью Фаулера здесь или столь же всеобъемлющий блог Марка Симана из Microsoft.

Оригинал: “https://dev.to/gervg/step-by-step-introduction-to-unit-testing-in-java-3ae7”