На каком шаге произошла ошибка

Основной инструмент при поиске ошибок в коде

Основной инструмент при поиске ошибок в коде

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

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

<?php
  function summa($x, $y) {
    $sum = $x + $y;
    return $x;
  }
  echo summa(5, 4);
?>

В результате, мы видим, что у нас вывелось 5, хотя мы хотели увидеть 9 (5 + 4). Данный код очень простой, поэтому, конечно, здесь Вы легко сразу обнаружите ошибку. Однако, данный пример является не более, чем примером. Его задача рассказать о механизме поиска ошибок в алгоритме. Так вот, мы должны выяснить, на каком шаге происходит ошибка. И это делается с помощью просмотра значения переменных на каждом шаге. Вот пример того, что нужно делать, чтобы найти ошибку:

<?php
  function summa($x, $y) {
    echo $x." - ";
    echo $y." - ";
    $sum = $x + $y;
    echo $sum;
    return $x;
  }
  echo summa(5, 4);
?>

Далее мы анализируем каждый вывод переменной:

  1. Так, переменная x выдала значение 5. Следовательно, эта переменная передалась верно.
  2. Переменная y имеет значение 4, что так же верно, ведь именно 4 мы и передавали.
  3. И, наконец, переменная sum показала значение 9. Это то самое значение, которое является истинным.

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

Итак, подведём итог. Основным инструментом поиска ошибок является оператор echo (либо функция print_r(), если переменная является массивом). Всё, что нужно, это просто смотреть на необходимую переменную на каждом шаге алгоритма. И понять, на каком моменте возникает ошибка. Если, допустим, ничего вообще не выводится, то это значит, что функция даже не вызывается. Следовательно, ищем ошибку там, где она должна была вызваться.

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

  • Создано 01.09.2011 23:17:13


  • Михаил Русаков

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

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

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

Основной инструмент при поиске ошибок в коде

Основной инструмент при поиске ошибок в коде

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

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

<?php
  function summa($x, $y) {
    $sum = $x + $y;
    return $x;
  }
  echo summa(5, 4);
?>

В результате, мы видим, что у нас вывелось 5, хотя мы хотели увидеть 9 (5 + 4). Данный код очень простой, поэтому, конечно, здесь Вы легко сразу обнаружите ошибку. Однако, данный пример является не более, чем примером. Его задача рассказать о механизме поиска ошибок в алгоритме. Так вот, мы должны выяснить, на каком шаге происходит ошибка. И это делается с помощью просмотра значения переменных на каждом шаге. Вот пример того, что нужно делать, чтобы найти ошибку:

<?php
  function summa($x, $y) {
    echo $x." - ";
    echo $y." - ";
    $sum = $x + $y;
    echo $sum;
    return $x;
  }
  echo summa(5, 4);
?>

Далее мы анализируем каждый вывод переменной:

  1. Так, переменная x выдала значение 5. Следовательно, эта переменная передалась верно.
  2. Переменная y имеет значение 4, что так же верно, ведь именно 4 мы и передавали.
  3. И, наконец, переменная sum показала значение 9. Это то самое значение, которое является истинным.

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

Итак, подведём итог. Основным инструментом поиска ошибок является оператор echo (либо функция print_r(), если переменная является массивом). Всё, что нужно, это просто смотреть на необходимую переменную на каждом шаге алгоритма. И понять, на каком моменте возникает ошибка. Если, допустим, ничего вообще не выводится, то это значит, что функция даже не вызывается. Следовательно, ищем ошибку там, где она должна была вызваться.

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

  • Создано 01.09.2011 23:17:13


  • Михаил Русаков

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

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

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Git. Инструкция по установке

Оглавление

  1. Windows
  2. Mac OS
  3. Linux

Windows

Скачайте установочный файл

Перейдите по ссылке Git Windows. Скачивание установочного exe-файла для вашей операционной системы должно начаться автоматически. Если этого не произошло, нажмите на ссылку «click here to download manually» (как показано на скриншоте):

1. Запустите установку

Запустите скачанный на предыдущем шаге exe-файл. Дождитесь появления экрана установки.

2. Лицензионное соглашение

На первом экране вам предложат согласиться с условиями лицензии GNU GPL. Внимательно их прочитайте, после чего нажмите кнопку Next (как показано на скриншоте):

3. Путь установки

Выберите путь для установки Git (лучше его оставить по умолчанию) и нажмите кнопку Next (как показано на скриншоте):

4. Компоненты для установки

Удостоверьтесь, что выбранные опции (флажки) соответствуют приведённым на скриншоте (они выбраны по умолчанию) и нажмите кнопку Next (как показано на скриншоте):

5. Имя пункта меню в Пуск

Оставьте значение по умолчанию Git и нажмите кнопку Next (как показано на скриншоте):

6. Редактор по умолчанию

Выбранный по умолчанию редактор (Vim) достаточно тяжёл для новичков, поэтому выберите из выпадающего списка опцию Nano Editor и нажмите кнопку Next (как показано на скриншоте):

Чуть позже в рамках нашего курса настроим Git на использование другого редактора.

7. Переменная окружения PATH

На данном этапе необходимо выбрать, добавлять ли Git в переменную окружения PATH. Это набор путей файловой системы, в которой ищутся запускаемые файлы. Если для вас это звучит не понятно — не расстраивайтесь, эта информация нам не особо нужна. Выберите опцию Windows Promt и нажмите кнопку Next (как показано на скриншоте):

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

8. HTTPS

Необходимо выбрать библиотеку, которая будет использована для HTTPS-соединений. Оставьте выбранной опцию OpenSSL и нажмите кнопку Next (как показано на скриншоте):

9. Символы окончания строки

Символы, обозначающие окончание строки различаются в Windows и Unix-подобных ОС (Mac OS, Linux, FreeBSD), поэтому выберите опцию Checkout Windows-style, commit Unix-style line-endings и нажмите кнопку Next (как показано на скриншоте):

10. Терминал

На данном экране вам предлагают выбрать какой терминал (командную строку) вы будете использовать с Git. Оставьте выбранной по умолчанию опцию MinTTY и нажмите кнопку Next (как показано на скриншоте):

11. git pull

Поведение по умолчанию для git pull. Оставьте выбранной опцию Default (fast-forward or merge) и нажмите кнопку Next (как показано на скриншоте):

12. Credential Manager

Выберите значение None и нажмите кнопку Next (как показано на скриншоте):

13. Extra-опции

Убедитесь, что установлен флажок только на Enable file system caching и нажмите кнопку Next (как показано на скриншоте):

14. Экспериментальные опции

Убедитесь, что все экспериментальные опции отключены и нажмите кнопку Install (как показано на скриншоте):

15. Завершение установки

Дождитесь завершения установки и нажмите кнопку Next (как показано на скриншоте):

Проверка установки

Кликните правой кнопкой мыши на любой папке в Windows, в открывшемся контекстном меню должны появиться две новых опции (как показано на скриншоте):

Выберите опцию Git Bash Here. Вы должны увидеть окошко, похожее на то, что показано на скриншоте:

Где user — имя вашего пользователя, desktop — имя вашего компьютера.

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

Если что-то пошло не так

Вы можете удалить Git через Панель Управления и установить его заново. В любом случае, обязательно сделайте скриншот ошибки и пришлите его и номер шага (на котором произошла ошибка) нашим ассистентам — они вам обязательно помогут.

Mac OS

Есть несколько вариантов:

  1. Если вы используете Homebrew
  2. Если вы не используете Homebrew

Установка через Hombebrew

Откройте терминал и запустите команду brew install git

Установка не через Homebrew

Скачайте установочный файл

Перейдите по ссылке Git Mac. Скачивание установочного dmg-файла для вашей операционной системы должно начаться автоматически. Если этого не произошло, нажмите на ссылку в разделе Building from Source (как показано на скриншоте).

Далее нажмите кнопку Download (как на скриншоте ниже)

1. Запустите установку

Откройте двойным кликом на скачанном dmg-файле установочный образ. Вы должны увидеть содержимое образа (как показано на скриншоте ниже). Удерживая клавишу Ctrl двойным кликом откройте установочный файл:

Если вы видите на экране окно, в котором нет кнопки открыть (как на скриншоте ниже), значит вы не удерживали клавишу Ctrl при открытии. Попробуйте ещё раз.

В открывшемся окне нажмите кнопку Открыть (как на скриншоте ниже).

2. Установите Git

В открывшемся окне нажмите кнопку Продолжить (как на скриншоте ниже).

Во всех остальных окнах оставьте настройки по умолчанию и нажимайте на кнопки Продолжить или Установить (как на скриншоте ниже).

По завершении установки нажмите на кнопку Закрыть (как на скриншоте ниже).

Проверка установки

Откройте программу Terminal и вбейте команду git --version, если вы увидели версию (как на скриншоте ниже), то установка прошла успешно. Обратите внимание, ваша версия может быть новее.

Linux

Откройте терминал и в зависимости от используемого вами дистрибутива запустите от лица суперпользователя команду на установку git:

  • Debian/Ubuntu: apt-get install git
  • Fedora: yum install git или dnf install git

Дождитесь окончания установки и введите команду: git --version

Если на экране отобразиться версия, то установка прошла успешно.

Если что-то пошло не так

Если вы застряли на каком-то шаге, произошла ошибка или что-то пошло не так, обязательно напишите аспирантам, приложив скриншот ошибки и указав на каком шаге она произошла. Мы вам обязательно поможем!

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

Вы также можете использовать конвейер обработки журналов, такой как стек ELK (Elasticsearch, Logstash и Kibana) или CloudWatch Insights, для сбора полезной статистики, например времени ответа.

Как бэкэнд-разработчик, я нашел эти 5 журналов наиболее полезными.

Запрос и ответ обработчика

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

Запросы и ответы сторонних служб

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

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

Работа над коллекциями: количество предметов, начало и конец работы над предметом

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

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

Однако регистрация количества элементов не загромождает журнал и поможет вам отлаживать и отслеживать различные сценарии.

Шаги обработки

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

Различные ветви If-Else

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

Правильный журнал ошибок

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

Отслеживание

Альтернативным решением для регистрации всего является использование системы отслеживания. Существует множество вариантов, включая решения для самостоятельного размещения, такие как Zipkin, и SaaS, например AWS X-Ray.

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

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

Выделение секций и шагов

Улучшаем код

В прошлом уроке мы написали тест на экран проверки доступности интернета, код тестового класса выглядел вот так:

package com.kaspersky.kaspresso.tutorial

import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Rule
import org.junit.Test

class WifiSampleTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        MainScreen {
            wifiActivityButton {
                isVisible()
                isClickable()
                click()
            }
        }
        WifiScreen {
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
            checkWifiButton.isVisible()
            checkWifiButton.isClickable()
            wifiStatus.hasEmptyText()
            checkWifiButton.click()
            wifiStatus.hasText(R.string.enabled_status)
            device.network.toggleWiFi(false)
            checkWifiButton.click()
            wifiStatus.hasText(R.string.disabled_status)
            device.exploit.rotate()
            wifiStatus.hasText(R.string.disabled_status)
        }
    }
}

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

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

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

package com.kaspersky.kaspresso.tutorial

import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Rule
import org.junit.Test

class WifiSampleWithStepsTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        // Step 1. Open target screen
        MainScreen {
            wifiActivityButton {
                isVisible()
                isClickable()
                click()
            }
        }
        WifiScreen {
            // Step 2. Check correct wifi status
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
            checkWifiButton.isVisible()
            checkWifiButton.isClickable()
            wifiStatus.hasEmptyText()
            checkWifiButton.click()
            wifiStatus.hasText(R.string.enabled_status)
            device.network.toggleWiFi(false)
            checkWifiButton.click()
            wifiStatus.hasText(R.string.disabled_status)

            // Step 3. Rotate device and check wifi status
            device.exploit.rotate()
            wifiStatus.hasText(R.string.disabled_status)
        }
    }
}

Это немного улучшит читаемость кода, но всех проблем не решит. Например, у вас какой-то тест упадет, как вы узнаете, на каком шаге это произошло? Вам придется исследовать логи, пытаясь понять, что пошло не так. Было бы гораздо лучше, если бы в логах отображались записи вроде Step 1 started -> ... -> Step 1 succeed или Step 2 started -> ... -> Step 2 failed. Это позволит немедленно определить по записям в логе, на каком этапе возникла проблема.

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

package com.kaspersky.kaspresso.tutorial

import android.util.Log
import androidx.test.core.app.takeScreenshot
import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Rule
import org.junit.Test

class WifiSampleWithStepsTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        try {
            Log.i("KASPRESSO", "Step 1. Open target screen -> started")
            MainScreen {
                wifiActivityButton {
                    isVisible()
                    isClickable()
                    click()
                }
            }
            Log.i("KASPRESSO", "Step 1. Open target screen -> succeed")
        } catch (e: Throwable) {
            Log.i("KASPRESSO", "Step 1. Open target screen -> failed")
            takeScreenshot()
        }
        WifiScreen {
            try {
                Log.i("KASPRESSO", "Step 2. Check correct wifi status -> started")
                device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
                checkWifiButton.isVisible()
                checkWifiButton.isClickable()
                wifiStatus.hasEmptyText()
                checkWifiButton.click()
                wifiStatus.hasText(R.string.enabled_status)
                device.network.toggleWiFi(false)
                checkWifiButton.click()
                wifiStatus.hasText(R.string.disabled_status)
                Log.i("KASPRESSO", "Step 2. Check correct wifi status -> succeed")
            } catch (e: Throwable) {
                Log.i("KASPRESSO", "Step 2. Check correct wifi status -> failed")
            }

            try {
                Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -> started")
                device.exploit.rotate()
                wifiStatus.hasText(R.string.disabled_status)
                Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -> succeed")
            } catch (e: Throwable) {
                Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -> failed")
                takeScreenshot()
            }
        }
    }
}

Давайте включим интернет на устройстве и проверим работу нашего теста.

Запускаем. Тест пройден успешно.

Теперь давайте посмотрим логи. Для этого откройте вкладку Logcat в нижней части Android Studio

Logcat

Здесь отображается множество логов и найти наши довольно сложно. Мы можем отфильтровать логи по тэгу, который указали («KASPRESSO»). Для этого кликните на стрелку в правой верхней части Logcat и выберите пункт Edit Configuration

Edit configuration

У вас откроется окно создания фильтра. Добавьте название фильтра, а также тэг, который нас интересует:

Create filter

Теперь у нас отображается только полезная информация. Давайте очистим лог

Clear logcat

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

Log step 1

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

Смотрим дальше:

Log step 2

Log step 2

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

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

Несмотря на то, что тест должен был завершиться с ошибкой, все тесты зеленые. Смотрим в лог — сейчас нас интересует step 2, который должен был завершиться неудачно из-за того, что изначально интернет на устройстве выключен

Log step 2 failed

Судя по логам, step 2 действительно завершился неудачно. Был проверен статус заголовка, текст не совпал, программа осуществила еще несколько попыток проверить, что текст на заголовке содержит текст enabled, но все эти попытки не увенчались успехом и шаг завершился с ошибкой. Почему в этом случае тесты у нас зеленые?

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

try {
        ...
} catch (e: Throwable) {
    /**
     * Мы обработали исключение и дальше оно проброшено не будет, поэтому такой 
     * тест считается выполненным успешно
     */
    Log.i("KASPRESSO", "Step 2. Check correct wifi status -> failed")
}

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

package com.kaspersky.kaspresso.tutorial

import android.util.Log
import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Rule
import org.junit.Test

class WifiSampleWithStepsTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        try {
            Log.i("KASPRESSO", "Step 1. Open target screen -> started")
            MainScreen {
                wifiActivityButton {
                    isVisible()
                    isClickable()
                    click()
                }
            }
            Log.i("KASPRESSO", "Step 1. Open target screen -> succeed")
        } catch (e: Throwable) {
            Log.i("KASPRESSO", "Step 1. Open target screen -> failed")
            throw e
        }
        WifiScreen {
            try {
                Log.i("KASPRESSO", "Step 2. Check correct wifi status -> started")
                device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
                checkWifiButton.isVisible()
                checkWifiButton.isClickable()
                wifiStatus.hasEmptyText()
                checkWifiButton.click()
                wifiStatus.hasText(R.string.enabled_status)
                device.network.toggleWiFi(false)
                checkWifiButton.click()
                wifiStatus.hasText(R.string.disabled_status)
                Log.i("KASPRESSO", "Step 2. Check correct wifi status -> succeed")
            } catch (e: Throwable) {
                Log.i("KASPRESSO", "Step 2. Check correct wifi status -> failed")
                throw e
            }

            try {
                Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -> started")
                device.exploit.rotate()
                wifiStatus.hasText(R.string.disabled_status)
                Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -> succeed")
            } catch (e: Throwable) {
                Log.i("KASPRESSO", "Step 3. Rotate device and check wifi status -> failed")
                throw e
            }
        }
    }
}

Запускаем тест еще раз. Теперь он завершается с ошибкой и мы имеем понятные логи, где сразу видно, на каком шаге произошла ошибка. После step 2 в логах больше ничего нет.

Код, который мы написали, рабочий, но очень громоздкий, и нам приходится для каждого шага писать целое полотно одинакового кода (логи, блоки try catch и т.д).

Для того чтобы упростить написание тестов и сделать код более читаемым и расширяемым, в Kaspresso были добавлены step-ы. У них «под капотом» реализовано все то, что мы сейчас писали вручную.

Чтобы использовать step-ы, необходимо вызвать метод run {} и в фигурных скобках перечислить все шаги, которые будут выполнены во время теста. Каждый шаг нужно вызывать внутри функции step.

Давайте напишем это в коде. Для начала удаляем все лишнее — логи и блоки try catch.

package com.kaspersky.kaspresso.tutorial

import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Rule
import org.junit.Test

class WifiSampleWithStepsTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        MainScreen {
            wifiActivityButton {
                isVisible()
                isClickable()
                click()
            }
        }

        WifiScreen {
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
            checkWifiButton.isVisible()
            checkWifiButton.isClickable()
            wifiStatus.hasEmptyText()
            checkWifiButton.click()
            wifiStatus.hasText(R.string.enabled_status)
            device.network.toggleWiFi(false)
            checkWifiButton.click()
            wifiStatus.hasText(R.string.disabled_status)

            device.exploit.rotate()
            wifiStatus.hasText(R.string.disabled_status)
        }
    }
}

Теперь в начале теста мы вызываем метод run, внутри которого для каждого шага вызываем функцию step. Этой функции в качестве параметра передаем название шага.

@Test
    fun test() {
        run {
            step("Open target screen") {
                ...
            }
            step("Check correct wifi status") {
                ...
            }
            step("Rotate device and check wifi status") {
                ...
            }
        }
    }

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

package com.kaspersky.kaspresso.tutorial

import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Rule
import org.junit.Test

class WifiSampleWithStepsTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        run {
            step("Open target screen") {
                MainScreen {
                    wifiActivityButton {
                        isVisible()
                        isClickable()
                        click()
                    }
                }
            }
            step("Check correct wifi status") {
                WifiScreen {
                    device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
                    checkWifiButton.isVisible()
                    checkWifiButton.isClickable()
                    wifiStatus.hasEmptyText()
                    checkWifiButton.click()
                    wifiStatus.hasText(R.string.enabled_status)
                    device.network.toggleWiFi(false)
                    checkWifiButton.click()
                    wifiStatus.hasText(R.string.disabled_status)
                }
            }
            step("Rotate device and check wifi status") {
                WifiScreen {
                    device.exploit.rotate()
                    wifiStatus.hasText(R.string.disabled_status)
                }
            }
        }
    }
}

Включаем интернет на устройстве и запускаем тест. Тест пройден успешно. Смотрим логи:

Log with steps

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

Давайте еще раз запустим этот тест теперь уже с выключенным интернетом. Тест падает. Смотрим логи.

Test fail with steps

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

Секции Before и After

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

В Kaspresso есть возможность добавить блоки before и after. Код внутри блока before будет выполняться перед тестом — здесь мы можем установить настройки по умолчанию. Код внутри блока after будет выполнен после теста. Во время выполнения теста состояние телефона может меняться: мы можем выключить интернет, сменить ориентацию, но после теста нужно вернуть исходное состояние. Делать это мы будем внутри блока after.

Тогда код теста будет выглядеть следующим образом:

package com.kaspersky.kaspresso.tutorial

import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Rule
import org.junit.Test

class WifiSampleWithStepsTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        before {
            /**
             * Перед тестом устанавливаем книжную ориентацию и включаем Wifi
             */
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
            device.network.toggleWiFi(true)
        }.after {
            /**
             * После теста возвращаем исходное состояние
             */
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
            device.network.toggleWiFi(true)
        }.run {
            step("Open target screen") {
                MainScreen {
                    wifiActivityButton {
                        isVisible()
                        isClickable()
                        click()
                    }
                }
            }
            step("Check correct wifi status") {
                WifiScreen {
                    checkWifiButton.isVisible()
                    checkWifiButton.isClickable()
                    wifiStatus.hasEmptyText()
                    checkWifiButton.click()
                    wifiStatus.hasText(R.string.enabled_status)
                    device.network.toggleWiFi(false)
                    checkWifiButton.click()
                    wifiStatus.hasText(R.string.disabled_status)
                }
            }
            step("Rotate device and check wifi status") {
                WifiScreen {
                    device.exploit.rotate()
                    wifiStatus.hasText(R.string.disabled_status)
                }
            }
        }
    }
}

Тест практически готов, можем добавить одно небольшое улучшение. Сейчас после переворота устройства мы проверяем, что текст остался прежним, но не проверяем, что ориентация действительно поменялась. Получается, что если метод device.expoit.rotate() по какой-то причине не сработал, то ориентация не поменяется и проверка на текст будет бесполезной. Давайте добавим проверку, что ориентация девайса стала альбомной.

Assert.assertTrue(device.context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)

Теперь полный код теста выглядит так:

package com.kaspersky.kaspresso.tutorial

import android.content.res.Configuration
import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.device.exploit.Exploit
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.kaspersky.kaspresso.tutorial.screen.MainScreen
import com.kaspersky.kaspresso.tutorial.screen.WifiScreen
import org.junit.Assert
import org.junit.Rule
import org.junit.Test

class WifiSampleWithStepsTest : TestCase() {

    @get:Rule
    val activityRule = activityScenarioRule<MainActivity>()

    @Test
    fun test() {
        before {
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
            device.network.toggleWiFi(true)
        }.after {
            device.exploit.setOrientation(Exploit.DeviceOrientation.Portrait)
            device.network.toggleWiFi(true)
        }.run {
            step("Open target screen") {
                MainScreen {
                    wifiActivityButton {
                        isVisible()
                        isClickable()
                        click()
                    }
                }
            }
            step("Check correct wifi status") {
                WifiScreen {
                    checkWifiButton.isVisible()
                    checkWifiButton.isClickable()
                    wifiStatus.hasEmptyText()
                    checkWifiButton.click()
                    wifiStatus.hasText(R.string.enabled_status)
                    device.network.toggleWiFi(false)
                    checkWifiButton.click()
                    wifiStatus.hasText(R.string.disabled_status)
                }
            }
            step("Rotate device and check wifi status") {
                WifiScreen {
                    device.exploit.rotate()
                    Assert.assertTrue(device.context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)
                    wifiStatus.hasText(R.string.disabled_status)
                }
            }
        }
    }
}

Итог

В этом уроке мы значительно улучшили наш код, он стал чище, понятнее, и его стало легче поддерживать. Это стало возможным благодаря таким функциям Kaspresso, как step, before и after. Также мы научились выводить сообщения в лог, а также читать логи, фильтровать их и анализировать.

Возможно, вам также будет интересно:

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

  • Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии