Часть 1.
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 Кбайт, вы можете сконцентрироваться на тестировании небольших файлов вместо того, чтобы браться за граничные случаи, в которых рассматриваются очень большие файлы.
Присоединяйтесь — мы покажем вам много интересного
Присоединяйтесь к ОК, чтобы подписаться на группу и комментировать публикации.
Нет комментариев