Часть 3. 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. Ошибки доступности. Часто системы будут работать правильно, когда пользователь использует стандартные системы ввода и вывода, но не когда пользователь попытается задействовать нестандартные устройства. Они зачастую необходимы для тех, кто не может работать с системой при помощи стандартного набора "клавиатура — мышь — монитор"; например, слепые пользователи, которые используют дисплей Брайля для чтения выходных данных с компьютера. Если ваша программа не работает корректно с такими системами, значит, зависящие от них пользователи не смогут пользоваться вашей программой. Убедитесь, что вы предоставляете несколько способов ввода и вывода или как минимум можете принимать и выводить обычный тест. Не все пользователи могут использовать мышь или видеть изображения. Не предполагайте, что ваши настройки подойдут для всех, кто использует программу.
Будни тестировщика
:Олег Петухов
Ошибки, которые следует искать.
Часть 3.
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. Ошибки доступности. Часто системы будут работать правильно, когда пользователь использует стандартные системы ввода и вывода, но не когда пользователь попытается задействовать нестандартные устройства. Они зачастую необходимы для тех, кто не может работать с системой при помощи стандартного набора "клавиатура — мышь — монитор"; например, слепые пользователи, которые используют дисплей Брайля для чтения выходных данных с компьютера. Если ваша программа не работает корректно с такими системами, значит, зависящие от них пользователи не смогут пользоваться вашей программой.
Убедитесь, что вы предоставляете несколько способов ввода и вывода или как минимум можете принимать и выводить обычный тест. Не все пользователи могут использовать мышь или видеть изображения. Не предполагайте, что ваши настройки подойдут для всех, кто использует программу.