1. Логические ошибки. Логическая ошибка является ошибкой в логике программы. Разработчик понимал, что нужно сделать, но в процессе преобразования системы от описания до имплементации что-то пошло не так. Это могло быть чем-то простым и вызванным случайной заменой "больше чем" (>) на "меньше чем" (<) или же сложным и вызванным запутанным взаимодействием между многочисленными переменными.
Для поиска логических ошибок следует убедиться, что ожидаемый результат достигается для различных входных значений. Границы — явные и неявные — зачастую оказываются "золотой жилой" для поиска дефектов. Также попробуйте другие виды интересных входных значений, такие как спецсимволы, чрезвычайно длинные строки и неправильно отформатированные данные. Входные данные, поступающие от других систем или пользователей, часто содержат странные или некорректные данные. Неплохо знать, что произойдет, когда ваша система получит их.
Для сложных выходных значений (например, генерация большой веб-страницы из шаблона) может оказаться невыполнимой проверка этих значений напрямую. Однако вы можете проверить наличие допустимых свойств выходных значений, и то, что эти свойства ожидаются. Например, что данные сгенерированной страницы могут быть разобраны, и что она отображает информацию корректно. Вы также можете взглянуть на данные с более высокого уровня абстракции — вместо проверки, что в HTML-коде тег <strong> оборачивает каждое встречающееся слово "кошка", может оказаться проще взглянуть на страницу, найти слова "кошка" и убедиться, что все они выделены полужирным.
2. Ошибки на единицу. Хотя технически это логические ошибки, они встречаются настолько часто, что их стоит отметить отдельно. Ошибка на единицу — это ситуация, когда программа делает что-то неправильно, потому что значение неверно всего на единицу. Это было причиной внимания при определении граничных значений - граничные значения являются целенаправленным методом поиска ошибок на единицу. Почему они такие распространенные? Представим очень простой метод, который определяет, является ли человек несовершеннолетним:
public Boolean isMinor(int personage) {
if (personage <= 18) {
return true; }
else {
return false;
}
}
Вы заметили ошибку? По крайней мере, в Соединенных Штатах на момент написания вы больше не считались несовершеннолетним в момент, когда вам исполнилось 18 лет. Из-за использования <= вместо < метод вернет, что человек несовершеннолетний, даже если ему исполнилось 18. Подобная незначительная ошибка может возникнуть в самых разных случаях — из-за того, что кто-то решил, что индексы массива начинаются с 1, а не с 0, что кто-то перепутал "больше или равен" с "больше", кто-то использовал ++i вместо i++ и т. п. Такие ошибки зачастую менее заметны, чем другие, т. к. они проявляются только в случае конкретных значений. При тестировании проверяйте граничные значения, и вы, скорее всего, обнаружите какие-то из "ошибок на единицу".
Ошибки округления и ошибки плавающей запятой (точкой). Компьютерные системы часто используют переменные с плавающей запятой для представления десятичных чисел (например, 1.1). Зачастую это гораздо более эффективно, чем использовать значения произвольной точности или рациональные числа, но эффективность достигается ценой потери точности. Например, представим, что тестируемая система использует стандарт IEEE 754 для хранения числа одинарной точности с плавающей запятой (т. е. 32-битного значения). Указывание 1.1 в качестве значения не означает, что оно и хранится как 1.1. Вместо этого оно сохраняется как 1.10000002384185791015625, потому что существует буквально бесконечное количество дробных чисел, и все их нужно сохранить в 32 битах. Если вы попытаетесь связать неограниченное количество значений c ограниченным пространством, то окажется, что некоторые значения имеют одинаковое представление. Если вы помните дискретную математику, то это принцип Дирихле (или "принцип ящиков и голубей" — pigeonhole principle) в действии. Это значение оказалось наиболее близким к 1.1. Сопоставление различных значений одному и тому же представлению по сути говорит, что значения будут округлены.
"И хорошо, - можете подумать вы. - Это очень небольшое различие между настоящим значением и его представлением. Я не могу представить себе, чтобы оно могло как-то оказать влияние". А что произойдет, если вы умножите его на другое число с плавающей запятой? Это различие может увеличиться. А затем вы умножите результат еще на одно число с плавающей запятой, а потом еще... И в итоге вы будете иметь дело со значениями, которые будут совсем не близки к тем, которые должны быть! Хотя постепенный дрейф значений может и не произойти, но поскольку некоторые значения-представления могут быть больше или меньше действительных значений, такая ситуация вполне вероятна.
Это является одной из причин, почему во многих языках программирования присутствует тип данных Currency (финансовый); представьте, если в банке подобные калькуляции окажутся неверными. Использование значений с плавающей запятой будет стоить им немало средств. И всё может оказаться даже еще хуже, и тут не надо ничего придумывать: почитайте про трагедию с ракетой Patriot в 1991 году. Накопившиеся ошибки плавающей запятой привели к неспособности американской ПВО во время войны в Персидском заливе обнаружить атаку ракетой Scud, что стало причиной гибели 28 человек. Еще около сотни получили ранения.
В некоторых случаях допустима осознанная потеря точности из-за отбрасывания ряда чисел или когда программа при расчетах округляет число вверх или вниз. Например, в какой-то момент программе может потребоваться сконвертировать десятичную дробь в целое число, и у программиста есть несколько способов, как сделать это. Преобразование может идти вниз (т. е. 4.8 становится 4 путем отбрасывания всего, что находится справа от десятичного разделителя), оно может идти вверх (т. е. преобразовывая 4.3 в 5), или же можно воспользоваться хорошо знакомым со школы округлением, когда 4.5 или большее округляется вверх, а меньшее, наоборот, вниз. Какой путь использовать? Это зависит от программы и ожидаемого результата, и довольно легко выбрать неправильный путь.
Для того чтобы осуществить проверку на ошибки округления и ошибки плавающей запятой, попробуйте провести тестирование с другими десятичными значениями в качестве входных данных. Убедитесь, что конечные выходные значения фактически соответствуют ожидаемым выходным значениям с учетом допустимой погрешности. Если входные данные могут взаимодействовать с другими данными, проверьте, что происходит при использовании больших объемов данных. Подобные виды ошибок будут накапливаться и таким образом становиться более уловимыми.
4. Интеграционные ошибки. Когда ошибки существуют в интерфейсе между двумя различными частями системы, они известны как интеграционные ошибки. Эти интерфейсы могут быть границами классов, границами пакетов или межпроцессными границами, вплоть до границ между большими мультикомпьютерными системами. Так как интерфейсы часто находятся там, где существуют разделения между командами или людьми, работающими с различными частями системы, эти интерфейсы окажутся средой, богатой на дефекты. В командах обычно лучше налажена коммуникация между членами команды; в конце концов, они работают над схожими частями системы и получают быструю обратную связь всякий раз, когда возникает проблема при работе с их конкретной подсистемой. При взаимодействии с другими командами возникновение недопонимания более вероятно, и это создает возможности для перепроверок, что система работает корректно, а сделанные предположения верны. Даже если имеется хорошо определенная спецификация интерфейса, существуют вероятности недопонимания спецификации или ошибок во время ее реализации.
Для тестировщика фокус на тестировании того, как системы интегрированы между собой, принесет большие дивиденды. Разработчикам зачастую сложно гарантировать то, что системы корректно взаимодействуют между собой, т. к. они, как правило, заняты какой-то отдельной частью разрабатываемой программы и не имеют всеохватывающего взгляда на систему.
5. Ошибки предположений. Практически невозможно полностью описать большинство систем при помощи требований. Если бы вы определили систему настолько точно, то это значит, что вы практически бы написали программу. Поэтому разработчики часто делают предположения о том, как программа должна функционировать. Однако эти предположения могут не совпадать с запросами потребителя или с тем, какого поведения от этой системы ожидают другие системы. Можно привести несколько распространенных примеров для проверки.
Как системе следует отображать ошибки?
Как данные должны отображаться или выводиться другим способом?
Существуют ли какие-то требования по форматированию файлов? • Какие системы должны поддерживаться?
С какими системами будет осуществляться взаимодействие?
Какие виды интерфейсов необходимы?
Какими должны быть пользовательский опыт и пользовательский интерфейс?
Как будет осуществляться доступ к системе? Кем?
Какие будут использоваться терминология, акронимы и т. п.?
Каковы допустимые диапазоны или пределы для данных?
Как будут вводиться данные? В каких форматах?
Если вы предполагаете, что все веса вводятся в фунтах, что произойдет, если кто-то другой сделает предположение о килограммах? Было сделано предположение, что выходные данные отображаются в ASCII, но какая-то часть данных была введена в UTF-8, что привело невозможности отобразить часть информации на экране? Разработчик написал программу, в которой используется интерфейс командной строки, а заказчик хотел графический интерфейс? Выходные файлы записаны в формате CSV (значения, разделенные запятыми), а конечные потребители ожидали использования табуляции в качестве разделителя? Все эти сделанные в процессе разработки предположения могут привести к дефектам во время использования системы
Также могут существовать общие требования для заданной области, о которых разработчик может не подозревать. В конце концов, разработчики, как правило, являются разработчиками, а не экспертами в той области, для которой они создают программное обеспечение. Конечно, это не всегда так, но очень часто будет существовать разрыв между знаниями человека, занимающегося разработкой системы, и представлениями конечного потребителя.
Как тестировщик ПО, вы можете и должны помогать сократить этот разрыв. Понимание требований пользователя и того, как разработчики пишут программы, может позволить вам увидеть расхождения и узнать, на что следует смотреть при разработке тест-планов. Это также поможет вам расставить приоритеты и продумать стратегию при определении того, какие функции и граничные случаи необходимо проверить. Например, если вы знаете, что размер некоего типа файлов, с которыми работает система, не превышает 50 Кбайт, вы можете сконцентрироваться на тестировании небольших файлов вместо того, чтобы браться за граничные случаи, в которых рассматриваются очень большие файлы.
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. Ошибки ввода-вывода. Данные внутри вашей программы живут в уютном маленьком подготовленном мире. Данные вне вашей программы злы и неконтролируемы, с клыками, когтями и не соблюдают никаких законов кроме законов Мерфи. Если вы читаете файл с диска, возможно, что этот файл не существует. Возможно, он существует, но не в том формате. Возможно, он правильного формата, но поврежден. Возможно, вы пытаетесь записать в него, но он только для чтения. Возможно, другой пользователь уже открыл его. Возможно, он в каталоге, доступа в который у пользователя нет. Возможно, у вас был доступ в этот каталог в момент запуска программы, но не теперь. Возможно, файл существует, но он пустой. Возможно, размер файла в сотни мегабайт, в то время как вы предполагали, что всего несколько килобайт. Этот перечисление можно продолжать и дальше.
Вам следует проверять, что, если вашей системе нужен доступ к диску, она подготовлена к подобным случайностям. Один из возможных путей — иметь специальный подкаталог для тестирования, заполненный самыми разными необычными файлами, и когда новой функции потребуется прочитать файл, запустить ее на всех этих экземплярах. Эти необычные файлы могут реализовывать упомянутые выше проблемные ситуации, а также те случаи, которые связаны со сферой работы программы (например, файлы с самореферентной внутренней структурой, содержащие отсутствующие данные и т. п.).
12. Ошибки интерфейса. Системам часто приходится взаимодействовать с другими системами, и для этого им необходим какой-то интерфейс. Этот интерфейс может быть более или менее определенным, от простого приема текста на входе и выдаче текста на выходе (как в большинстве утилит UNIX, таких как more или grep) до работы со сложными бинарными форматами. Однако этот интерфейс должен быть определен на каком-то уровне, и существует вероятность, что определение было дано неоднозначно, или различные части определения противоречат друг другу, или же разные участники команды разработчиков интерфейса допускали разные предположения при его создании (см. выше об ошибках предположений). Есть даже вероятность, что были допущены ошибки во время программирования интерфейса!
Эти интерфейсы не обязательно должны быть межпроцессными или межкомпьютерными. Это может быть такая низкоуровневая модель, как интерфейс между двумя классами. Интерфейс метода обычно легко понять; например, в Java легко разобраться, что именно следующий метод принимает в качестве аргументов и что выдает, даже без всяких комментариев:
public boolean greaterThanTen(int a, int b) {
return (a + b) > 10;
}
В данном случае вы можете увидеть, что метод принимает два целых значения, а возвращает булево. Довольно легко понять, что именно делает метод, даже без разъяснений. Хотя всегда существует вероятность (намеренно или случайно) написать запутанный код, положиться на побочные эффекты и глобальные переменные, но мозг среднестатистического программиста неплох в понимании вещей на уровне методов. Правда, как только вы, поднимаетесь на уровень классов, становится сложнее сказать, как и почему вещи взаимодействуют между собой. Вы больше не смотрите на сам язык, фокусируясь на таких ключевых словах, как for, if и return, которые знакомы вам, как ваше детское одеяло. Вместо этого вы видите "метаязык" класса, который окажется новым для вас. Вы работаете на более высоком уровне абстракции, и все будет становиться еще хуже по мере вашего продвижения вверх. Решением станет правильное определение и дизайн интерфейсов, но то, что понятно одному человеку, другого заставит вскинуть руки в отчаянии. Интерфейсы, как правило, проводят демаркационную линию между разработчиками или командами разработчиков. Тем самым они создают плодородную почву для непонимания, а там, где есть непонимание, также имеются и дефекты.
Во время тестирования вам необходимо потратить время на исследование интерфейсов системы. Существуют области, где коммуникации могли быть нарушены или где люди по разным сторонам сделали различные предположения. Что происходит, когда вы попытаетесь передать неожиданные значения? Что произойдет, если не передать ожидаемое значение, или передано больше значений, чем ожидается? Что произойдет, если данные лежат вне диапазона или слишком большие?
13. Ошибки нулевого указателя (Null pointer). Возможно, вы счастливчик и работали с языком, в котором отсутствует концепция нулевого указателя, но если вы программируете на Java, то наверняка знакомы с ней даже лучше, чем вам кажется. Каждый раз, когда объект может быть нулевым, должна осуществляться явная проверка, что он таковым не является, перед тем, как вы попытаетесь к нему обратиться. В 2012 году в разговоре об истории концепции нулевых объектов Франклин Чен (Franklin Chen) заметил, что в Java-подобных языках у любого объекта всегда два возможных состояния — сам объект и нулевая версия его самого1 . Вы не можете предположить, например, что если у вас есть объект Integer, то он на самом деле является Integer; это может быть нулевой объект, маскирующийся как целый.
Это сравнительно легко заметить, если вы осуществляете тестирование белого ящика и видите сам код. Если же вы осуществляете тестирование черного ящика, вам придется подумать также о случаях, когда объект может не существовать. Что произойдет, если вы попытаетесь искать объект в базе, которая не существует? Что произойдет, если вы ничего не введете в текстовое поле? Что произойдет, когда вы зададите неправильный ID? Для многих программ поведение при подобных ситуациях должно быть заранее явно определено. Каждый раз, когда обычное поведение должно быть проверено на однозначность, существует вероятность, что программист забыл это сделать или сделал неправильно.
Для углубленного анализа "проблемы на миллиард долларов" нулевых указателей ознакомьтесь со статьей Франклина Чена "Исторические, теоретические, сравнительные, философские и практические перспективы" (Franklin Chen "Historical, Theoretical, Comparative, Philosophical, and Practical Perspectives") по адресу: https://franklinchen.com/blog/2012/09/06/my-pittsburgh-ruby-talk-nil/.
14. Ошибки распределенных систем. Тестирование системы, которая запускается одновременно на нескольких серверах, имеет свои особенности. Во многих случаях не существует "истинной" копии данных (единого места, где, как предполагается, данные всегда корректные, в отличие от мест, содержащих "копии", которые могут быть устаревшими или неправильными). Вам предстоит позаботиться не только о тестировании системы с различными программными настройками и оборудованием, но и проверить различные сетевые топологии и разные комбинации настроек ПО и оборудования. Различные уровни пропускной способности канала и задержек прохождения пакетов приведут к тому, что дефекты проявят себя. Время и отметки времени могут отличаться от системы к системе, несколько человек могут редактировать одни данные с разных машин, разные машины могут иметь разные концепции текущего состояния данных и т. д. Существует множество ситуаций, в которых определение ожидаемого поведения может оказаться сложным, а то и вообще невозможным.
При тестировании распределенной системы вам нужно потратить время на проверку того, что системы синхронизированы правильно; данные должны быть согласованными (по крайней мере, в конечном итоге). Убедитесь, что система работает, когда изменяется одна из ее составляющих, и особенно когда выходит из строя одна из отдельных систем (а это почти наверняка когда-то произойдет — чем больше машин выполняют ваш код, тем более вероятно, что по крайней мере одна из них выйдет из строя). Прочитайте статью Питера Дейтча "Восемь ошибок распределенных вычислений" (Peter Deutsch "The Eight Fallacies of Distributed Computing"1 ) — не беспокойтесь, читается очень быстро - и подумайте о тех предположениях, которые сделали вы и разработчики системы, а затем... сломайте эти предположения.
15. Ошибки конфигурации. Ошибки конфигурации могут проявляться двумя разными способами. Первый — когда администраторы системы могут сконфигурировать систему по-разному. Например, при настройке приложения Rails можно задать многочисленные параметры в различных конфигурационных YAML-файлах. У многих приложений имеются настройки конфигурации, ключи командной строки или другие способы изменения работы, и иногда они переопределяют друг друга или же взаимодействуют между собой самыми невообразимыми способами.
Перейдем ко второму виду ошибок конфигурации. У работающих с онлайнсистемой пользователей компьютеры могут быть настроены по-разному. Например, у них могут быть установлены разные браузеры — от мощных, выпущенных крупными компаниями и организациями, до небольших текстовых. У этих браузеров свои уровни совместимости со стандартами. Пользователи будут запускать эти браузеры на различных операционных системах, с разным железом и программным обеспечением, с различными плагинами. У некоторых пользователей JavaScript включен, у других — нет; некоторые используют блокировщики рекламы; у кого-то отключена загрузка изображений; у некоторых в браузерах включена функция запрета отслеживания, у некоторых нет — это перечисление можно продолжать. Всегда существует вероятность того, что проблема кроется в особой конфигурации.
Будет ли ваша система работать соответствующим образом при всех возможных конфигурациях, которые пользователи используют для доступа (т. е. разные браузеры, отключенный JavaScript, без картинок и т. д.)? Выдаст ли система соответствующую информацию об ошибке, если она (система) сконфигурирована неправильно? Имеются ли у нее разумные настройки по умолчанию (или предупреждает ли она пользователя в зависимости от требований и области применения) в случае, если какое-то значение в настройках отсутствует или неверное? Имеются ли какие-то настройки, которые перекрывают другие неочевидным образом или вызывают проблемы при определенных комбинациях?
16. Ошибки доступности. Часто системы будут работать правильно, когда пользователь использует стандартные системы ввода и вывода, но не когда пользователь попытается задействовать нестандартные устройства. Они зачастую необходимы для тех, кто не может работать с системой при помощи стандартного набора "клавиатура — мышь — монитор"; например, слепые пользователи, которые используют дисплей Брайля для чтения выходных данных с компьютера. Если ваша программа не работает корректно с такими системами, значит, зависящие от них пользователи не смогут пользоваться вашей программой.
Убедитесь, что вы предоставляете несколько способов ввода и вывода или как минимум можете принимать и выводить обычный тест. Не все пользователи могут использовать мышь или видеть изображения. Не предполагайте, что ваши настройки подойдут для всех, кто использует программу.