Повышаем эффективность ручного тестирования в Linux в оболочке bash

Linux - де-факто является мировым стандартом для серверных операционных систем (хотя необходимость размещать серверные компоненты на Windows-серверах всё же существует и её нельзя отрицать). В виду этого многие тест-инженеры тратят значительную часть своего времени, выполняя задачи ручного тестирования в Linux средах с использованием удаленного подключения по ssh через терминал. Существует ряд принципов, команд и хитростей, которые могут помочь повысить эффективность выполнения подобных задач. О них мы сегодня и поговорим:

Рассмотрим каждый пункт более детально.

Понимание концепции привилегий и разрешений

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

Если вы создадите файл в каком-ибо каталоге и затем взглянете на содержимое этого каталога командой ls -la, вы скорее всего увидите что-то подобное:

drwxr-xr-x 2 alexey alexey 4096 may 29 14:38 .
drwxr-xr-x 3 alexey alexey 4096 may 29 14:38 ..
-rw-r--r-- 1 alexey alexey    0 may 29 14:38 myFile

Вывод, который мы видим, состоит из 7 колонок:

  1. Параметры доступа к файлу (разрешения)

  2. Количество "жёстких" ссылок к элементу

  3. Имя пользователя, ассоциированное с элементом

  4. Группа пользователей, ассоциированная с элементом

  5. Размер элемента

  6. Дата последнего изменения элемента

  7. Имя элемента

Кроме самого файла, в результирующем списке мы видим еще два элемента: . и ... Элемент . определяет текущий каталог, в котором находятся просматриваемые файлы. Элемент .. определяет родительский каталог в котором находится просматриваемый каталог.

Нам, в основном, интересно содержимое колонок 1, 3 и 4 потому что как раз они и определяют кто может иметь доступ к файлу, а кто нет.

Каждое значение в колонке 1 состоит из 10 позиций. Первая позиция показывает нам является ли запись каталогом или нет. Для каталогов на первой позиции стоит значение d.

Что всё это означает?

Следующие 9 позиций разделены на 3 группы привилегий. Первая группа определяет что владелец (колонка номер 3) файла может с ним делать, вторая группа определяет что может делать с файлом группа пользователей (колонка номер 4), ассоциированная с этим файлом, третья же группа привилегий определяет что могут делать с файлом все остальные пользователи.

Каждая группа привилегий состоит из 3-х позиций: разрешение на чтение (read), разрешение на запись (write) и разрешение на выполнение (execute). Если разрешение включено, позиция заполняется соответствующей буквой, если нет - прочерком (-).

Если разрешение на выполнение (execute) активировано для каталога, это означает, что пользователь может в этот каталог "зайти".

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

Изменение привилегий в Linux

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

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

sudo chgrp webelementclick myFile
ls -la
total 8
drwxr-xr-x 2 alexey alexey          4096 may 29 14:38 .
drwxr-xr-x 3 alexey alexey          4096 may 29 14:38 ..
-rw-r--r-- 1 alexey webelementclick    0 may 29 14:38 myFile

Мы можем увидеть, что единственной вещью, которая поменялась стала группа, ассоциированная с нашим файлом (колонка 4). Теперь нам необходимо изменить привилегии, используя команду chmod. Но как мы сообщим Linux какие разрешения включить, а какие выключить?

Сообщаем мы это передавая команде chmod соответствующие аргумент. Аргумент этот имеет формат XYZ. Где X определяет чьи привилегии мы будем менять. Возможно одно из трёх значений: u означает, что мы будем менять привилегии владеющего файлом пользователя (user); g означает, что менять мы будем привилегии, данные ассоциированной с файлом группе пользователей (group); o - все остальные пользователя (other users). Y может принимать значение + если мы хотим добавить привилегию, либо - если мы хотим убрать привилегию. Z определяет какую именно привилегию мы хотим изменить. Для Z возможны следующие значения: r - хотим изменить привилегию чтения (read), w - хотим изменить привилегию записи (write), x - хотим изменить привилегию запуска (execute).

В нашем примере нам необходимо убрать привилегию чтения у остальных пользователей. Что означает, что мы должны выполнить команду chmod o-r myFile которую можно прочитать как "Изменить привилегии для файла myFile таким образом, что все остальные пользователи (o), которые не являются владельцем файла alexey, а также не входят в группу пользователей webelementclick потеряют привилегию (-) на чтение (r) данного файла".

Давайте ещё раз взглянем на содержимое нашего каталога:

drwxr-xr-x 2 alexey alexey          4096 may 29 14:38 .
drwxr-xr-x 3 alexey alexey          4096 may 29 14:38 ..
-rw-r----- 1 alexey webelementclick    0 may 29 14:38 myFile

Мы можем видеть, что ключ r, который заполнял 8-ю позицию, теперь изменился на -, что означает, что "все остальные пользователи" не имеют никакого доступа к файлу (---).

Эффективная навигация по каталогам

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

alexey@master-host:~$ pwd
/home/alexey

Автозаполнение при помощи кнопки Tab

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

Если вы начнете набирать имя файла или название команды, и затем нажмете tab, bash найдет все файлы в текущем каталоге, начинающиеся с введенной вами части (либо доступные команды) и автоматически дополнит введенную часть до места, где в результирующем наборе появится расхождение. Если результат автозаполнения не соответствует команде, которую вы хотите набрать, вам следует добавить символы, которые помогут оболочке различить какую команду из нескольких подходящих вы имели в виду.

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

Допустим, что в нашем распоряжении есть такой каталог:

drwxr-xr-x 2 alexey alexey          4096 may 30 13:29 .
drwxr-xr-x 3 alexey alexey          4096 may 29 14:38 ..
-rw-r----- 1 alexey webelementclick    0 may 29 14:38 myFile
-rw-r--r-- 1 alexey alexey             0 may 30 13:29 myFile.backup

Предположим также, что мы хотели бы просмотреть содержимое файла myFile.backup, используя для этого команду cat. Мы можем набрать в командной строке часть команды cat my и нажать клавишу tab. После нажатия на tab, bash автоматически дополнит команду до cat myFile, т.к. ему не известно имели ли мы в виду файл myFile, или файл myFile.backup потому как оба этих файла начинаются с одинаковой последовательности символов. Т.к. мы хотим указать второй файл, мы добавляем к тому, что уже заполнила система, символ . (при этом введенная часть команды в командной строке будет выглядеть так: cat myFile.) и снова нажимаем клавишу tab. Теперь bash точно знает что мы имеем ввиду, ведь других файлов, начинающихся с myFile. в каталоге нет. Это позволяет ему дополнить команду до конца.

Овладев этой нехитрой техникой, вы сможете многократно повысить эффективность работы с файлами и командами.

Навигационные ярлыки (шорткаты)

Linux (кое-где в паре с оболочкой bash) предоставляет ряд "ярлыков", упрощающих навигацию по структуре каталогов системы. Такими "шорткатами" являются ~ (способ, предоставляемый оболочкой bash), . и ... Шорткат ~ ведет в домашний каталог текущего пользователя. Если в вашем домашнем каталоге есть файл (например myFile), то доступ к нему вы сможете получить используя путь ~/myFile в каком бы каталоге вы в данный момент не находились.

Шорткаты . и .. уже обсуждались ранее. Они ссылаются на текущий и на родительский каталоги соответственно. Вы можете комбинировать эти шорткаты для построения эффективных путей. Например, если вы хотите перейти в родительский каталог вашего домашнего каталога, вы можете использовать команду cd ~/.. (опять же из любого текущего положения).

Вы можете создавать свои собственные ярлыки, называемые "мягкими ссылками" (soft links) или "символьными ссылками" (symbolic links). Хорошем решением является размещение таких ссылок в домашнем каталоге (доступном через шорткат ~).

Например, представьте, что существует некий каталог, расположенный где-то в глубине файловой структуры (/mnt/mywork/mainfolder), и вам необходимо часто выполнять в нем некоторые задачи. Вы можете создать ссылку, которая бы располагалась в домашнем каталоге и имела бы простое имя (например - main) используя следующую команду:

ln -s /mnt/mywork/mainfolder ~/main

Теперь вы можете перейти в каталог /mnt/mywork/mainfolder из любого места, используя команду cd ~/main

Объединение нескольких команд в одну

Когда инженер-тестировщик выполняет задачи ручного тестирования в операционной системе Linux, часто бывает полезным объединять несколько команд в одну таким образом, что выход предыдущей команды будет сразу подаваться на вход следующей. Такой вид склеивания выполняется при помощи символа |.

Наиболее часто встречающейся задачей, где такая склейка может пригодиться, является фильтрация вывода с использованием команды grep. Например команда cat mylog | grep error будет разбита на две части: cat mylog выведет содержимое файла mylog. Команда grep error отфильтрует все данные поданные на вход таким образом, что на выходе останутся, только строки, содержащие подстроку error. Символ | означает, что данные, выведенные командой cat mylog будут перенаправлены на вход команды grep error.

Такое объединение может включать сколь угодно много звеньев. Например при тестировании часто бывает необходимым просматривать файлы (в том числе и уже отфильтрованные), размером многократно превышающие размеры окна терминала. Для больших файлов имеет смысл использовать команду less, которая позволяет прокручивать содержимое файла используя клавиши Up, Down, PgUp и PgDown. В таком случае команда может выглядеть так:

cat mylog | grep error | less

Пример выше состоит из трёх подкоманд: cat mylog выводит всё содержимое файла, grep error оставляет только строки включающие подстроку error, а less берёт отфильтрованный вывод и оборачивает его в удобный механизм прокрутки.

Анализ иерархии процессов

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

Для команды ps существует специальный ключ --forest, сообщающий утилите, что вывод необходимо отформатировать в виде иерархии. Например результат выполнения команды ps -a --forest будет выглядеть так:

  PID TTY          TIME CMD
28670 pts/0    00:00:00 ps
 1957 tty2     00:00:00 gnome-session-b
 2061 tty2     00:05:35  \_ gnome-shell
 2106 tty2     00:00:09  |   \_ ibus-daemon
 2110 tty2     00:00:00  |   |   \_ ibus-dconf
 2362 tty2     00:00:02  |   |   \_ ibus-engine-sim
 5706 tty2     00:00:00  |   \_ idea.sh
 5771 tty2     00:15:33  |       \_ java
 5855 tty2     00:00:03  |           \_ fsnotifier64
 6237 tty2     00:00:13  |           \_ java
27809 tty2     00:00:05  |           \_ java
 2182 tty2     00:00:00  \_ gsd-power
 2183 tty2     00:00:00  \_ gsd-print-notif

Если дерево слишком велико, вы опять же можете объединить команду вывода списка процессов с командой less, как было показано в ранее: ps -a --forest | less. Это позволит вам использовать удобный механизм прокрутки результата вывода команды ps.

Мониторинг содержимого каталога

Ещё одной стандартной задачей при выполнении ручного тестирования является мониторинг содержимого каталога. К примеру, вам может быть интересен момент появления некоторого файла в каталоге или изменение свойств такого файла. Linux предоставляет инструмент наблюдения за чем либо - команду watch. Такая команда принимает любую другую в качестве аргумента и последовательно её запускает с некоторыми заданными интервалами (равными по умолчанию 2-м секундам).

В случае необходимости использовать её для мониторинга содержимого каталога, вы можете выполнить следующую команду в командной строке вашего терминала: watch ls -la.

Использование переменных окружения

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

Наверняка вы ранее встречались с подобными переменными (такими переменными являются, например, JAVA_HOME или PATH). Если вы выполняете задачи ручного тестирования, вам, возможно, приходилось сталкиваться с необходимостью запуска тестируемого приложения с различным набором значений переменных окружения. Далее мы взглянем на ряд моментов, позволяющих повысить эффективность взаимодействия с этим инструментом.

Пять вещей, которые необходимо знать при работе с переменными окружения

  1. Значение для переменной окружения устанавливаемся с помощью команды export в формате export VAR_NAME=VAR_VALUE.

  2. При задании значения для переменной окружения могут быть использованы значения других переменных. Например export MY_VAR=Hello установит значение Hello для переменной с именем MY_VAR. Дальнейшее выполнение команды export MY_OTHER_VAR="$MY_VAR, World!" установит значение Hello, World! для переменной MY_OTHER_VAR.

  3. Если вы используете значение некоторой переменной внутри значения некоторой другой переменной, может случиться так, что вам понадобится добавить символы в значение новой переменной сразу после значения существующей. Для того чтобы дать знать оболочке bash где заканчивается имя переменной, а где начинается добавленный текст, используется следующая конструкция: export MY_VAR=Hel, export MY_OTHER_VAR=${MY_VAR}lo.

  4. Значение переменной можно получить либо обращением $VAR_NAME либо обращением ${VAR_NAME} (см. пример выше). Например, вы можете вывести значение переменной MY_VAR, используя команду echo одним из следующий способов: либо echo $MY_VAR либо echo ${MY_VAR}.

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

Использование эффективных фильтров с grep

Мы уже упоминали команду grep когда говорили об объединении команд. Команда grep в повседневной практике ручного тестирования в Linux встречается, пожалуй, чаще остальных. В комбинации с cat (вывод содержимого файлов) и, возможно, less (вывод в режиме прокрутки содержимого), команда grep предлагает действительно мощный механизм для анализа логов или конфигурационных файлов. Ниже я покажу несколько хитростей, которые помогут вам повысить эффективность выполнения подобных задач.

В качестве примера мы будем использовать файл myTest.log со следующим содержимым:

DEBUG	13:43:59	Running in dev mode
INFO	13:44:01	Reading value a: 5.25
INFO	13:44:02	Reading value b: 0.0
INFO	13:44:02	Evaluating division
ERROR	13:44:03	Division by zero error!
INFO	13:44:04	Sending email report..
DEBUG	13:44:04	Mail subj: "INFORMATION ABOUT SERVICE FAILURE"

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

Простой случай

Вы можете посмотреть на все записи, содержащие подстроку DEBUG используя следующую команду:

cat myTest.log | grep DEBUG

Очень просто. По умолчанию поиск регистрозависимый. Это означает, что команда выше найдет записи, содержащие слово DEBUG, но не найдет записей со словом debug (в нижнем регистре). Чтобы изменить такое поведение, вы можете использовать ключ -i. Таким образом, для того, чтобы найти и записи с DEBUG и записи с debug, вам будет необходимо выполнить такую команду:

cat myTest.log | grep -i DEBUG

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

cat myTest.log | grep "Evaluating division"

Отображаем близлежащие данные

При помощи grep вы можете отобразить некоторый контекст для каждого результата, найденного командой. Используя ключи -B (before) и -A (after) вы можете указать количество строк, отображаемых до и после найденной по указанному условию информации. Например, если вы хотите отображать 3 строчки до и 1 строчку после каждой записи, содержащей подстроку ERROR, вы можете использовать такую команду:

cat myTest.log | grep -B3 -A1 ERROR

Усиливаем поиск регулярными выражениями

Поисковая строка, передаваемая в grep в качестве аргумента, может быть трактована утилитой несколькими различными способами: либо как фиксированная строка либо как регулярное выражение. Кроме того, регулярные выражения также могут делиться на различные типы. В различных реализациях утилиты grep может поддерживаться различный набор таких типов (воспользуйтесь командой man grep для получения помощи по вашей версии утилиты). Мы же будем говорить о реализации, называемой GNU grep. Такая утилита поставляется с большинством свободно распространяемых сборок ОС Linux.

Пример, описанный выше, содержит намеренную ловушку. Когда мы используем команду cat myTest.log | grep DEBUG мы получаем то, что ожидаем потому как слово DEBUG появляется только в колонке, отвечающей за тип сообщения. Однако, если бы мы использовали тот же самый запрос, но для типа сообщений INFO мы бы получили такой результат:

INFO	13:44:01	Reading value a: 5.25
INFO	13:44:02	Reading value b: 0.0
INFO	13:44:02	Evaluating division
INFO	13:44:04	Sending email report..
DEBUG	13:44:04	Mail subj: "INFORMATION ABOUT SERVICE FAILURE"

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

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

Следующая команда покажет вам только те записи, которые начинаются с подстроки INFO (символ ^ означает начало строки, поэтому ^INFO не сматчится с последней строкой, представляющей дебаг-сообщение):

cat myTest.log | grep ^INFO

А вот более сложный пример, отображающий все записи, имеющие временную отметку 13:44 (здесь мы используем классы символов):

cat myTest.log | grep ^[[:alpha:]]*[[:space:]]*13:44

Кстати, ничто не мешает вам объединять команду grep с другими командами grep. Например:

cat myTest.log | grep ^INFO | grep [[:digit:]]$

покажет вам записи, начинающиеся с INFO и заканчивающиеся цифрой ($ обозначает конец строки).

"Трогаем" файлы с touch. Зачем нам это нужно?

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

Такого рода функция весьма полезна когда ваше приложение следит за фактом изменения файла. Дело в том, что чаще всего изменение файла определяется фактом изменения времени его модификации (иначе приложению бы пришлось хранить где-то копию файла и непрерывно выполнять сравнение с ней). К примеру, такой подход использует JBoss. Если вы хостите своё приложение (например myapp.ear) в этом контейнере, и вам необходимо передеплоить его, вам надо просто зайти в каталог deployments и выполнить там команду touch myapp.ear.

Мониторинг логов при помощи tail

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

  1. Используйте ключ -F (аналог использования двух ключей --follow=name --retry). Он сообщит tail о необходимости обновлять вывод при поступлении в файл новых данных, а также ждать если возникли проблемы чтения из файла. Последняя особенность важна, когда вы мониторите логи, ограниченные размером. В таком случае, при достижении предельного размера, лог переименовывается и создается новый с таким же именем. Без использования ключа --retry ваша утилита tail "потеряла" бы файл и перестала следить за ним.

  2. Объединяйте tail и grep чтобы вывод команды tail фильтровался бы в реальном времени.

Пример объединения команд:

tail -F myapp.log | grep ^INFO

Поиск дополнительных логов

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

В каталоге /var/log Linux хранит логи, которые заполняются специальным системным сервисом. В принципе, любое приложение может обращаться к этому сервису и через него добавлять в логи свои сообщения. Если что-то пошло не так, существует вероятность, что информацию о проблеме можно будет почерпнуть из таких логов.

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

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