Часть 2. 6. Ошибки отсутствующих данных. Каждый раз, когда данные приходят из внешнего источника в программу (например, CSV-файла, сервиса API или напрямую от пользователя, который вводит данные в терминале), существует вероятность того, что необходимые данные будут отсутствовать. Это может произойти как по такой простой причине, когда пользователь случайно нажал клавишу , так и по довольно сложной, например где-то внутри огромного JSON-ответа отсутствовал нужный атрибут. Хотя в любом случае система должна обрабатывать такие ситуации надлежащим образом. Однако всегда допускайте, что внешний источник может не предоставить все данные, которые вам необходимы. Дальнейшие действия, когда данные отсутствуют, зависят от программы и области ее применения. В некоторых случаях программа может благополучно проигнорировать такую ситуацию; в других случаях ситуацию нужно отметить или записать в лог-файл, или же можно показать предупреждающее сообщение пользователю; в редких случаях правильным решением может оказаться выключение всей системы. Однако вы должны знать, как система отреагирует на отсутствие данных, и убедиться, что она действует так при возникновении подобной ситуации. 7. Ошибки плохих данных. Ошибки плохих данных вызывают еще больше проблем, чем ошибки отсутствующих данных. В то время как при поиске последних бывает достаточно проверить один элемент на наличие определенного атрибута, существует бесконечное количество способов для данных оказаться "плохими". Эти данные могут быть сгенерированы внутри системы, но более вероятно появление плохих данных из внешних систем, которые имеют различные допущения, используют разные форматы, были повреждены или модифицированы каким-либо образом и т.д. Данные слишком большие. Возможно, система может обработать данные до определенного размера. Что произойдет, если размер входных данных окажется больше? Данные слишком короткие. С другой стороны, что если система странно обрабатывает малые объемы данных? Это не такая частая проблема, как проблема больших данных, но зачастую она является причиной неэффективности (например, система всегда резервирует мегабайт памяти по умолчанию для входных данных, даже если их размер составляет всего несколько байт). Данные отформатированы некорректно. Что произойдет, если программа ожидает данные, разделенные запятой, а получает данные с табуляцией в качестве разделителя? Что произойдет, если ваша программа ожидает строку JSON, а получает XML? Данные находятся вне допустимого диапазона. Что произойдет, если программу запросят информацию о 593 штате Америки? Что произойдет, если указанная температура составит –500 °C (что ниже абсолютного нуля и соответственно не встречается в этой Вселенной)? Данные были повреждены. Существуют ли какие-то гарантии при приеме данных, позволяющие сказать, что эти данные правильные и не были модифицированы? Хотя проблема повреждения данных не так актуальна, как раньше, все равно возможно возникновение ошибок. Например, если ктонибудь откроет файл в текстовом редакторе другой операционной системы, что приведет к конвертации символов переноса строки в неожиданные знаки? Несогласованные данные. Что произойдет, если входные данные противоречат сами себе? Например, если вы получаете данные с двумя записями для ID 723, и в одной из них указывается имя John Doe, а в другой — Jane Doe? Вы знаете, какое должно быть ожидаемое поведение в этой ситуации? Данные не поддаются анализу. Что произойдет, если вы получаете данные, которые не могут быть разобраны из-за отсутствия закрывающего символа > или ) или в которых содержится циклическая ссылка? Выдаст ли система соответствующее сообщение об ошибке или поймет, что оказалась застрявшей в бесконечной петле? Отслеживание проблем плохих данных может быть сложным, т. к. существует масса возможностей возникновения ошибок, и зачастую только весьма специфическая конфигурация плохих данных может вызвать проблему. Хотя, конечно, можно подготовить данные, содержащие всевозможные ошибки, но более эффективно использовать нечеткое тестирование (или фаззинг, от англ. fuzz testing) — отправлять случайно сгенерированные данные (которые могут соответствовать или не соответствовать ожидаемым входным данным) и убеждаться, что система продолжает работать правильно. Смотри о стохастическом тестировании, чтобы узнать больше о фаззинге. 8. Ошибки отображения. Даже если система вычислит корректное значение, оно может быть отображено неверно. Такая проблема может возникнуть, если число или строка слишком длинные для отображения полностью (например, результат деления 1 на 3) и часть символов отбрасывается. В других случаях отбрасывания может не быть, и данное значение может "наползти" на другой текст или вызвать искажения вывода на экране прочей информации. Если вы отображаете значения на диаграмме, выбросы по осям x и y могут не попасть на экран, или же если попадут, станут причиной проблем с отображением других значений. Графика может выводиться некорректно, также возможно использование неверной битовой карты или цветов. Попытка вывести некоторые символы на дисплей может привести к зависанию терминала, включению сигнала динамика или к каким-либо проблемам с дисплеем. Неправильно обработанный HTML-код станет причиной того, что ваша веб-страница перестанет отображаться. Каждый раз, когда отображаются данные, проверьте не только то, что значение было вычислено правильно, но и то, что оно отображается соответственно. Используя данные, которые содержат необычные символы либо чрезвычайно большие или малые значения либо вообще не содержат ожидаемых значений, вы можете быть уверены, что символ, который нужно отобразить на экране, - это действительно тот символ, который увидит пользователь. 9. Ошибки внедрения (инъекций). Ошибки внедрения, которые являются подмножеством ошибок плохих данных, являются исполняемым кодом или прочими инструкциями, которые передаются программе. Если программу можно обмануть и заставить исполнить эти инструкции, последствия могут оказаться ужасными и включать в себя потерю или повреждение данных, неавторизованный доступ к системе или просто вывод системы из строя. Наиболее часто такую проблему могут вызвать управляющие и необычные символы. Управляющий код, который не улавливается интерфейсом ввода данных, может быть распознан другой подсистемой, и наоборот. Символы могут использоваться различными подсистемами и языками для различных целей. Например, в Java строки могут содержать нулевые символы (null), в то время как для строк языка C нулевой символ означает конец строки. Перед проведением тестирования определите, что произойдет, когда различные виды кода передаются программе. Это необязательно должна быть Java (или тот язык, на котором написана система). Веб-приложение может исполнять JavaScript-код в браузере посетителей. Многие системы используют SQL для запросов к базе данных и ее обновлений, и произвольный SQL-код может изменить или удалить данные. Внедряемый код может быть помещен в конце длинной строки — хитрость, часто используемая для того, чтобы воспользоваться переполнением буфера (см. главу о тестировании безопасности). Проверка всех этих потенциальных опасностей потребует тестирования с широким диапазоном входных значений, и существуют противники, которые выиграют, если вы пропустите хотя бы одну точку входа для их злоумышленных программ. 10. Сетевые ошибки. Хотя все компьютеры все больше и больше взаимодействуют друг с другом через сети, не везде сетевые соединения доступны или функционируют без сбоев. Система должна продолжать работу, даже если сетевое соединение временно потеряно. Конечно, некоторые системы требуют наличия сетевого соединения (работа в ssh-клиенте или веб-браузере покажется довольно скучной, если вы не можете подключиться к другим системам), но определенно они не должны падать или зависать без него. Возможно, самым драматичным примером тестирования сетевых ошибок является "тест топора" (упомянутый выше), в котором тестировщик берет топор и обрубает кабель, соединяющий систему с сетью. Это можно сымитировать без риска травм путем отключения кабеля или отключения сигнала Wi-Fi в самый разгар работы программы. Потеря сетевого соединения является не единственной возможной проблемой, которая способна возникнуть у работающей в сети программы. Вы можете также проверить, что именно произойдет в случае возникновения больших задержек при прохождении сетевых пакетов или чрезвычайно низкой пропускной способности. Зачастую программа может предположить, что существующего соединения достаточно, и продолжить работу, но в итоге работа системы окажется нестабильной, т. к. качество имеющегося соединения является очень низким. Согласно другому сценарию качество соединения может оказаться изменяющимся (частые разрывы и соединения), что может вызвать проблемы, которые не видны в случае одной долгой потери связи с сетью. Для сетевого соединения с высоким уровнем потери пакетов можно создать свой тест, особенно если вы используете UDP или другой протокол без установления соединения. Для реального теста вы можете добавить искажения в линию. Во всех этих ситуациях и в случае других сетевых проблем качество работы системы должно ухудшаться постепенно, а не приводить к мгновенному ее отключению. 11. Ошибки ввода-вывода. Данные внутри вашей программы живут в уютном маленьком подготовленном мире. Данные вне вашей программы злы и неконтролируемы, с клыками, когтями и не соблюдают никаких законов кроме законов Мерфи. Если вы читаете файл с диска, возможно, что этот файл не существует. Возможно, он существует, но не в том формате. Возможно, он правильного формата, но поврежден. Возможно, вы пытаетесь записать в него, но он только для чтения. Возможно, другой пользователь уже открыл его. Возможно, он в каталоге, доступа в который у пользователя нет. Возможно, у вас был доступ в этот каталог в момент запуска программы, но не теперь. Возможно, файл существует, но он пустой. Возможно, размер файла в сотни мегабайт, в то время как вы предполагали, что всего несколько килобайт. Этот перечисление можно продолжать и дальше. Вам следует проверять, что, если вашей системе нужен доступ к диску, она подготовлена к подобным случайностям. Один из возможных путей — иметь специальный подкаталог для тестирования, заполненный самыми разными необычными файлами, и когда новой функции потребуется прочитать файл, запустить ее на всех этих экземплярах. Эти необычные файлы могут реализовывать упомянутые выше проблемные ситуации, а также те случаи, которые связаны со сферой работы программы (например, файлы с самореферентной внутренней структурой, содержащие отсутствующие данные и т. п.).
Будни тестировщика
:Олег Петухов
Ошибки, которые следует искать.
Часть 2.
6. Ошибки отсутствующих данных. Каждый раз, когда данные приходят из внешнего источника в программу (например, CSV-файла, сервиса API или напрямую от пользователя, который вводит данные в терминале), существует вероятность того, что необходимые данные будут отсутствовать. Это может произойти как по такой простой причине, когда пользователь случайно нажал клавишу , так и по довольно сложной, например где-то внутри огромного JSON-ответа отсутствовал нужный атрибут. Хотя в любом случае система должна обрабатывать такие ситуации надлежащим образом. Однако всегда допускайте, что внешний источник может не предоставить все данные, которые вам необходимы.
Дальнейшие действия, когда данные отсутствуют, зависят от программы и области ее применения. В некоторых случаях программа может благополучно проигнорировать такую ситуацию; в других случаях ситуацию нужно отметить или записать в лог-файл, или же можно показать предупреждающее сообщение пользователю; в редких случаях правильным решением может оказаться выключение всей системы. Однако вы должны знать, как система отреагирует на отсутствие данных, и убедиться, что она действует так при возникновении подобной ситуации.
7. Ошибки плохих данных. Ошибки плохих данных вызывают еще больше проблем, чем ошибки отсутствующих данных. В то время как при поиске последних бывает достаточно проверить один элемент на наличие определенного атрибута, существует бесконечное количество способов для данных оказаться "плохими". Эти данные могут быть сгенерированы внутри системы, но более вероятно появление плохих данных из внешних систем, которые имеют различные допущения, используют разные форматы, были повреждены или модифицированы каким-либо образом и т.д.
Данные слишком большие. Возможно, система может обработать данные до определенного размера. Что произойдет, если размер входных данных окажется больше?
Данные слишком короткие. С другой стороны, что если система странно обрабатывает малые объемы данных? Это не такая частая проблема, как проблема больших данных, но зачастую она является причиной неэффективности (например, система всегда резервирует мегабайт памяти по умолчанию для входных данных, даже если их размер составляет всего несколько байт).
Данные отформатированы некорректно. Что произойдет, если программа ожидает данные, разделенные запятой, а получает данные с табуляцией в качестве разделителя? Что произойдет, если ваша программа ожидает строку JSON, а получает XML?
Данные находятся вне допустимого диапазона. Что произойдет, если программу запросят информацию о 593 штате Америки? Что произойдет, если указанная температура составит –500 °C (что ниже абсолютного нуля и соответственно не встречается в этой Вселенной)?
Данные были повреждены. Существуют ли какие-то гарантии при приеме данных, позволяющие сказать, что эти данные правильные и не были модифицированы? Хотя проблема повреждения данных не так актуальна, как раньше, все равно возможно возникновение ошибок. Например, если ктонибудь откроет файл в текстовом редакторе другой операционной системы, что приведет к конвертации символов переноса строки в неожиданные знаки?
Несогласованные данные. Что произойдет, если входные данные противоречат сами себе? Например, если вы получаете данные с двумя записями для ID 723, и в одной из них указывается имя John Doe, а в другой — Jane Doe? Вы знаете, какое должно быть ожидаемое поведение в этой ситуации?
Данные не поддаются анализу. Что произойдет, если вы получаете данные, которые не могут быть разобраны из-за отсутствия закрывающего символа > или ) или в которых содержится циклическая ссылка? Выдаст ли система соответствующее сообщение об ошибке или поймет, что оказалась застрявшей в бесконечной петле?
Отслеживание проблем плохих данных может быть сложным, т. к. существует масса возможностей возникновения ошибок, и зачастую только весьма специфическая конфигурация плохих данных может вызвать проблему. Хотя, конечно, можно подготовить данные, содержащие всевозможные ошибки, но более эффективно использовать нечеткое тестирование (или фаззинг, от англ. fuzz testing) — отправлять случайно сгенерированные данные (которые могут соответствовать или не соответствовать ожидаемым входным данным) и убеждаться, что система продолжает работать правильно. Смотри о стохастическом тестировании, чтобы узнать больше о фаззинге.
8. Ошибки отображения. Даже если система вычислит корректное значение, оно может быть отображено неверно. Такая проблема может возникнуть, если число или строка слишком длинные для отображения полностью (например, результат деления 1 на 3) и часть символов отбрасывается. В других случаях отбрасывания может не быть, и данное значение может "наползти" на другой текст или вызвать искажения вывода на экране прочей информации. Если вы отображаете значения на диаграмме, выбросы по осям x и y могут не попасть на экран, или же если попадут, станут причиной проблем с отображением других значений. Графика может выводиться некорректно, также возможно использование неверной битовой карты или цветов. Попытка вывести некоторые символы на дисплей может привести к зависанию терминала, включению сигнала динамика или к каким-либо проблемам с дисплеем. Неправильно обработанный HTML-код станет причиной того, что ваша веб-страница перестанет отображаться.
Каждый раз, когда отображаются данные, проверьте не только то, что значение было вычислено правильно, но и то, что оно отображается соответственно. Используя данные, которые содержат необычные символы либо чрезвычайно большие или малые значения либо вообще не содержат ожидаемых значений, вы можете быть уверены, что символ, который нужно отобразить на экране, - это действительно тот символ, который увидит пользователь.
9. Ошибки внедрения (инъекций). Ошибки внедрения, которые являются подмножеством ошибок плохих данных, являются исполняемым кодом или прочими инструкциями, которые передаются программе. Если программу можно обмануть и заставить исполнить эти инструкции, последствия могут оказаться ужасными и включать в себя потерю или повреждение данных, неавторизованный доступ к системе или просто вывод системы из строя.
Наиболее часто такую проблему могут вызвать управляющие и необычные символы. Управляющий код, который не улавливается интерфейсом ввода данных, может быть распознан другой подсистемой, и наоборот. Символы могут использоваться различными подсистемами и языками для различных целей. Например, в Java строки могут содержать нулевые символы (null), в то время как для строк языка C нулевой символ означает конец строки.
Перед проведением тестирования определите, что произойдет, когда различные виды кода передаются программе. Это необязательно должна быть Java (или тот язык, на котором написана система). Веб-приложение может исполнять JavaScript-код в браузере посетителей. Многие системы используют SQL для запросов к базе данных и ее обновлений, и произвольный SQL-код может изменить или удалить данные. Внедряемый код может быть помещен в конце длинной строки — хитрость, часто используемая для того, чтобы воспользоваться переполнением буфера (см. главу о тестировании безопасности). Проверка всех этих потенциальных опасностей потребует тестирования с широким диапазоном входных значений, и существуют противники, которые выиграют, если вы пропустите хотя бы одну точку входа для их злоумышленных программ.
10. Сетевые ошибки. Хотя все компьютеры все больше и больше взаимодействуют друг с другом через сети, не везде сетевые соединения доступны или функционируют без сбоев. Система должна продолжать работу, даже если сетевое соединение временно потеряно. Конечно, некоторые системы требуют наличия сетевого соединения (работа в ssh-клиенте или веб-браузере покажется довольно скучной, если вы не можете подключиться к другим системам), но определенно они не должны падать или зависать без него.
Возможно, самым драматичным примером тестирования сетевых ошибок является "тест топора" (упомянутый выше), в котором тестировщик берет топор и обрубает кабель, соединяющий систему с сетью. Это можно сымитировать без риска травм путем отключения кабеля или отключения сигнала Wi-Fi в самый разгар работы программы.
Потеря сетевого соединения является не единственной возможной проблемой, которая способна возникнуть у работающей в сети программы. Вы можете также проверить, что именно произойдет в случае возникновения больших задержек при прохождении сетевых пакетов или чрезвычайно низкой пропускной способности. Зачастую программа может предположить, что существующего соединения достаточно, и продолжить работу, но в итоге работа системы окажется нестабильной, т. к. качество имеющегося соединения является очень низким. Согласно другому сценарию качество соединения может оказаться изменяющимся (частые разрывы и соединения), что может вызвать проблемы, которые не видны в случае одной долгой потери связи с сетью. Для сетевого соединения с высоким уровнем потери пакетов можно создать свой тест, особенно если вы используете UDP или другой протокол без установления соединения. Для реального теста вы можете добавить искажения в линию. Во всех этих ситуациях и в случае других сетевых проблем качество работы системы должно ухудшаться постепенно, а не приводить к мгновенному ее отключению.
11. Ошибки ввода-вывода. Данные внутри вашей программы живут в уютном маленьком подготовленном мире. Данные вне вашей программы злы и неконтролируемы, с клыками, когтями и не соблюдают никаких законов кроме законов Мерфи. Если вы читаете файл с диска, возможно, что этот файл не существует. Возможно, он существует, но не в том формате. Возможно, он правильного формата, но поврежден. Возможно, вы пытаетесь записать в него, но он только для чтения. Возможно, другой пользователь уже открыл его. Возможно, он в каталоге, доступа в который у пользователя нет. Возможно, у вас был доступ в этот каталог в момент запуска программы, но не теперь. Возможно, файл существует, но он пустой. Возможно, размер файла в сотни мегабайт, в то время как вы предполагали, что всего несколько килобайт. Этот перечисление можно продолжать и дальше.
Вам следует проверять, что, если вашей системе нужен доступ к диску, она подготовлена к подобным случайностям. Один из возможных путей — иметь специальный подкаталог для тестирования, заполненный самыми разными необычными файлами, и когда новой функции потребуется прочитать файл, запустить ее на всех этих экземплярах. Эти необычные файлы могут реализовывать упомянутые выше проблемные ситуации, а также те случаи, которые связаны со сферой работы программы (например, файлы с самореферентной внутренней структурой, содержащие отсутствующие данные и т. п.).