- •4.9.1. Изменяемость
- •4.9.2. Классы операций
- •98 Глава 4
- •4.9.3. Полнота
- •100 Глава 4
- •4.9.5. Операции egual, similar и copy
- •102 Глава 4
- •104 Глава 4
- •5. Исключительные ситуации
- •108 Глава 5
- •110 Глава 5
- •6.2.1. Сигнализация об исключительных ситуациях
- •5.2.2. Обработка исключительных ситуаций
- •112 Глава 5
- •5.2.3. Предложение resignal
- •6.2.4. Необрабатываемые исключительные ситуации
- •114 Глава 5
- •6.2.3. Предложение resignal
- •5.2.4. Необрабатываемые исключительные ситуации
- •116 Глава 5
- •118 Глава 5
- •118 Глава 5
- •120 Глава 5
- •122 Глава 5
- •124 Глава 5
- •128 Глава 6
- •130 Глава 6
- •6.2.1. Реализация итераторов
- •6.2.2. Использование итераторов
- •132 Глава 6
- •134 Глава 6
- •136 Глава 6
- •138 Глава 6
- •140 Глава 7
- •142 Глава 7
- •7.2. Абстракция данных
- •144 Глава 7
- •146 Глава 7
- •148 Глава 7
- •150 Глава 7
- •7.4. Генераторы
- •162 Глава 7
- •154 Глава 7
- •156 Глава 7
- •162 Глава 8
- •164 Глава 8
- •166 Глава 8
- •168 Глава 8
- •170 Глава 9
- •172 Глава 9
- •174 Глава 9
- •176 Глава 9
- •9.1.3. Пример
- •178 Глава 9
- •180 Глава 9
- •182 Глава 9
- •184 Глава 9
- •186 Глава 9
- •188 Глава 9
- •190 Глава 9
- •192 Глава. 9
- •194 Глава 9
180 Глава 9
цикле мы должны проверить случай с двухэлементным массивом, когда искомый элемент отсутствует, является первым или же вторым. (Эти случаи невозможно получить анализом только спецификации. На уровне спецификации рассматривается только принадлежность элемента массиву. Позиция его в массиве интереса не представляет.) Аналогично в операции delete мы должны быть в состоянии удалить как первый, так и второй элементы массива. При этом мы учитываем возможность потенциальной ошибки, при которой последний элемент массива удаляется неправильно. В рассматриваемой реализации операции delete перед сжатием массива последний его элемент пересылается в позицию удаленного элемента. Если мы выполним эти действия в обратном порядке, то в том случае, когда удаляемый элемент находился на верхней границе массива, программа работать не будет.
intset = cluster is create, insert, delete, member, size, elements rep == array [int]
create =r proc ( ) returns (cvt) return (rep$new ( )) end create
insert == proc (s: intset, x: int)
it— member (s, x) then rep$addh (down (s), x) end end insert
delete = proc (s: cvt, x: int) for j: int in rep$indexes (s) do it s [j] = x then s [j] :== rep$top (s)
rep$remh (s) end end end delete
member = proc (s: cvt, x: int) returns (bool) for y: int in rep$elements (s) do if у = x then return (true) end end return (false) end member
size == proc (s; cvt) returns (int) return (rep$size (s)) end size
elements = iter (s: cvt) yields (int) for y: in rep$elements (s) do yield (y) end end elements
end intset Рис. 9.4. Реализация целочисленного набора intset.
Тестирование и отладка
9.2. Индивидуальное и интегральное тестирование
Тестирование обычно разбивается на две фазы. В процессе индивидуального тестирования устанавливается правильность работы каждого модуля отдельно от остальных. При интегральном тестировании проверяется одновременная работа всех модулей, т. е. всей программы.
Интегральное тестирование обычно более сложно, чем индивидуальное. Во-первых, охарактеризовать предполагаемое поведение программы целиком обычно более затруднительно, чем каждого отдельно взятого ее модуля. Во-вторых, При интегральном тестировании объем работы обычно резко возрастает. В частности, значительно увеличивается время выполнения теста. Наконец, в этих двух методах существенно отличаются друг от друга роли спецификаций.
В отличие от интегрального тестирования при индивидуальном тестировании, как правило, предполагается, что спецификация составлена правильно. При возникновении ошибки в процессе индивидуального тестирования мы делаем заключение, что неправильно работает сам модуль. При оценке всей программы мы должны учитывать тот факт, что наиболее серьезными ошибками обычно являются ошибки спецификации. В таких случаях каждый модуль выполняет возложенную на него функцию, однако сама программа — нет. Это в большинстве случаев свидетельствует об ошибках в спецификации. В такой ситуации тестируемый своими создателями модуль обычно функционирует правильно, однако при этом не соответствует требованиям тех, кто составлял модули, вызывающие данный. На этапе интегрального тестирования это чрезвычайно затрудняет поиск ошибок.
Рассмотрим программу, реализованную модулем Р, который вызывает модуль Q. При индивидуальной проверке Р и Q были проверены отдельно. (Как уже говорилось, для индивидуальной их проверки достаточно смоделировать поведение другого модуля.) Затем мы выполняем совместный тест, проверяя, удовлетворяют ли они спецификации для Р. При этом используются тесты модуля Р. Предположим, что была обнаружена ошибка. Возможны два случая.
1. Модуль Q проверяется с входными значениями, которые не были учтены при индивидуальной проверке.
2. Модуль Q ведет себя не в соответствии с предположением, выдвинутым при тестировании Р.
При работе с модулями, подобными Р и Q, возникает желание сразу проверить их работу совместно, не тестируя каждый в отдельности. Иногда совместные тесты более оправданны, но обычно лучше проводить индивидуальную проверку. Например, для программы, приведенной на рис. 9.1, мы беспокоимся о проверке
