Тестирование софта - статьи

       

Формализация стандартов


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

Стандарты на программные интерфейсы чаще всего состоят из двух частей: обоснования (rationale), представляющего целостную картину основных концепций и функций в рамках данного стандарта, и справочника (reference), описывающего каждый элемент интерфейса отдельно. В дополнение к элементам интерфейса (типам данных, функциям, константам и глобальным переменным) справочник может описывать и более крупные компоненты: подсистемы, заголовочные файлы и пр. Отдельные разделы справочника могут ссылаться друг на друга и содержать части друг друга.

Формализация стандарта включает несколько видов деятельности.

  1. Декомпозиция стандарта. Так как количество интерфейсных элементов, описанных в стандарте, может быть очень велико, то на первом шаге они разбиваются на логически связанные группы. Такая группа обычно состоит из операций и типов данных, связанных с определенным набором близких функций, и замкнута относительно обращения операций. Например, операции открытия и закрытия файлов, создания и удаления объектов нужно помещать в одну группу. Вся дальнейшая работа производится в рамках таких групп.
  2. Выделение требований. Следующая задача заключается в выделении всех требований стандарта к описываемым им элементам интерфейса, которые могут быть проверены. Так как одна и та же вещь может быть описана в нескольких местах, то все соответствующие разделы стандарта необходимо внимательно, фраза за фразой, прочитать и пометить все найденные требования и ограничения. Затем выделенные требования объединяются в непротиворечивый набор.
    Эта работа достаточно утомительная и тяжелая, но ни в коем случае не механическая. Ее сложность связана с преобразованием неформальных требований и ограничений в формальные, а, по словам М. Р. Шуры-Буры, переход от неформального к формальному существенно неформален. Утверждения и ограничения из разных частей стандарта могут быть несовместимыми, двусмысленными, представлять похожие, но не одинаковые идеи. Только текста стандарта часто не достаточно, чтобы прийти к непротиворечивой полной картине. Поэтому при выделении требований необходимо пользоваться общими знаниями о предметной области, книгами и статьями по тематике, знанием о используемых в существующих системах решениях, а также общаться с авторами стандарта, экспертами в области и опытными разработчиками. Некоторые аспекты специально не определяются стандартом точно, для того чтобы реализации различных производителей соответствовали ему. Занятный пример можно найти в текущей версии стандарта POSIX []. Описание функций fstatvfs() и statvfs() из sys/statvfs.h говорит: "Не специфицировано, имеют ли все члены структуры statvfs смысл для всех файловых систем" ("It is unspecified whether all members of the statvfs structure have meaningful values on all file systems"). Существует два возможных пути разрешения подобных ситуаций.
    • Можно ничего не проверять. В этом случае никаких требований из соответствующей части стандарта не извлекается.
    • Если существует небольшое количество возможных реализаций, то требования к ним могут быть представлены в виде параметризированных ограничений. При этом вводится конфигурационный параметр, разные значения которого соответствуют различным возможным реализациям и определяют конечный вид проверяемого требования. Пример из описания функции basename() в стандарте POSIX: "Если строка, на которую указывает параметр path, равна "//", то реализация может возвращать как '/', так и "//"" ("If the string pointed to by path is exactly "//", it is implementation-defined whether '/' or "//" is returned").
    Выделение требований проводится до тех пор, пока весь текст стандарта, касающийся выбранной группы элементов интерфейса, не будет разбит на требования, которые можно проверить, и остальные фразы, не содержащие проверяемых утверждений.


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


  3. Разработка спецификаций. Эта работа обычно выполняется параллельно и вперемешку с предыдущей. Они разделены только в целях ясности изложения. Большинство найденных требований записываются в форме контрактных спецификаций операций, типов данных и элементов данных. Каждая операция описывается с помощью предусловия и постусловия. Предусловие операции определяет область ее определения. Постусловие определяет ограничения на результаты работы операции и итоговое состояние системы в зависимости от значений ее входных параметров и состояния системы до ее вызова. Для типов данных и элементов данных определяются ограничения целостности, называемые инвариантами. Те требования, которые не оформляются в виде контрактных спецификаций, делятся на следующие группы.
    • Непроверяемые требования. Часть требований не проверяется, потому что их вообще нельзя проверить с помощью конечной процедуры тестирования или потому что не существует методов надежной проверки таких требований (например, "функция должна выдавать результат за время, меньшее, чем любой многочлен второй степени от значения ее параметра"), а также из-за крайней неэффективности их проверки ("возвращаемый функцией указатель должен отличаться от указателя на любой объект, присутствовавший в системе до ее вызова").
    • Проверяемые требования. Другая часть требований проверяется не в спецификациях по соображениям эффективности тестов и удобства их модификации.


      Ряд требований, касающихся целостности данных некоторых типов, может быть отражен в самом описании их структуры в спецификациях. При этом проверка соответствующих ограничений происходит при преобразовании из данных реализации в модельные данные. Если эти ограничения нарушаются, такое преобразование становится невозможным и спецификация не может с ними работать, но сообщение об их проверке и обнаруженном несоответствии появится в трассе теста. Некоторые требования можно проконтролировать только в результате многократных обращений к тестируемой функции и проверки каких-то свойств всей совокупности полученных при этом результатов. Эти требования часто неудобно оформлять в виде спецификаций - для них создается отдельный тестовый сценарий (см. ниже),
    • который производит все нужные обращения и проверяет необходимые свойства полученных результатов. Примером такой ситуации может служить требование к функции rand() генерировать при многократных обращениях последовательность случайных чисел, равномерно распределенных на некотором интервале. Для проверки этого свойства можно накопить данные о результатах ее вызовов и применить к их совокупности, например, критерий Колмогорова.
    Эта деятельность имеет два результата.
    • Формальные спецификации всех элементов интерфейса. Код спецификаций размечается, чтобы указать связь между описанными формальными ограничениями и соответствующими им требованиями из каталога требований.
    • Конфигурационная система стандарта. Эта система состоит из набора конфигурационных параметров, как объявленных в стандарте, так и дополнительно введенных разработчиками спецификаций, зависимостей между ними, а также связей между ними, элементами интерфейса и проверяемыми ограничениями. Некоторые конфигурационные опции, представленные как значения параметров, управляют набором проверяемых требований. Другие могут говорить о том, что определенная функциональность вообще отсутствует в системе, и соответствующие операции не должны вызываться. Третьи могут влиять на возможные коды ошибок, возвращаемые операциями. Пример первого случая можно увидеть в описании функции pthread_create() из стандарта POSIX: "Если макрос _POSIX_THREAD_CPUTIME определен, то новый поток должен иметь доступ к часам процессорного времени, и начальное значение этих часов должно быть выставлено в ноль" ("If _POSIX_THREAD_CPUTIME is defined, the new thread shall have a CPU-time clock accessible, and the initial value of this clock shall be set to zero").
  4. Определение критериев покрытия. Последний вид деятельности в рамках формализации стандарта заключается в определении критериев покрытия, которые могут быть использованы для измерения адекватности тестирования реализации на соответствие стандарту.Базовый критерий - это необходимость покрытия всех требований стандарта, применимых к текущей конфигурации тестируемой системы. Более строгие критерии могут определять дополнительные ситуации для тестирования. Все эти критерии базируются на спецификациях, представляющих требования стандарта. Критерии покрытия сложным образом связаны с конфигурационными параметрами. Возможность покрытия определенной ситуации может зависеть от текущих значений конфигурационных параметров, и эта зависимость должна быть зафиксирована, чтобы избежать многочисленных трудностей при анализе результатов тестирования. Результатом этой работы является набор критериев покрытия, тесно связанный с конфигурационной системой.
Процесс формализации стандарта и последующей разработки тестов на основе ее результатов проиллюстрирован на .

Содержание раздела