Лекция 14.11.2001-21.11.2001

Тестирование и Формальные спецификации

Фазы разработки программного продукта:

·  Определение требований(на этой фазе Формальные спецификации помогают)

·  Эскизное проектирование(концепции и “наброски” по разработке, недоспецификации)

·  Детальное проектирование(аксиомы, неявные спецификации, выработка пре и постусловий)

·  Реверс-инженерия(цель этапа- извлечение знаний и понимание, что и как делает система )

Знания материализуются в мозгу у разработчика. Человек не может хранить и обозревать одновременно большие объёмы информации. Однако, свои знания должен хранить без искажений. Для этого и используются формальные спецификации(ФС)

·  Документация пользователя (любой, кто будет читать ФС должен однозначно понимать/трактовать документацию )

·  Документация сопровождения (описание интерфейсов взаимодействия между модулями системы и описание, дающее общее представление о работе системы)

·  Тестирование (как проверить однозначность правильной работы системы? Надо знать, с чем сравнивать результат)

Можно сказать, что в этих вопросах использования ФС есть две задачи:

·  Максимум - поддерживать все артефакты системы в согласованном состоянии

·  Минимум - выделить минимальный набор тех артефактов, из которых следуют все остальные(некий базис).

Одного вида артефактов – недостаточно.

Задумаемся, какие артефакты нам обязательно нужны.

1.  Определение требований

2.  Необходима дополнительная информация для проверки требований.

НЕ нашли? Не то? Что вы ищете?

3.  Машинно-читаемые ФС в отличие от словесных спецификаций имеют машинную поддержку. Из ФС можно сгенерировать тесты.

4.  Нельзя узнать из реализации, а верен ли результат, возвращаемый функцией. Нельзя проверить, а полной ли функциональностью обладает система. Например, программист не реализовал несколько функций, это можно узнать, лишь сверившись с ФС.

5.  Врозможность проверить полноту системы.

Термины

Верификация - проверка соответствия процесса разработки требованиям к нему(а так ли мы ведем разработку, как нам предписано)

Валидация – проверка, что результат разработки ведет в соответствии с требованиями и ожиданиями пользователей.

Валидацию рассматриваем как характеристику продукта. Система делает то, что от нее требуется.

Тестирование- оценка качества программного обеспечивания(ПО) методом экспериментальной проверки, то есть путем исполнения тестов.

Нас интересует качество ПО, особенно функциональность.

Подходы к тестированию.

·  “черный ящик”- строим тест без знания о внутренней структуре реализации системы. Неважно, как придумали тот или иной тест, но чтобы проверить, как хорошо он покрывает код, надо запустить профилировщик, и он покажет количество задействованных строк программы.

·  “белый ящик”- наоборот, знаем детали реализации системы, и с учетом этого строим тесты, стараясь задействовать все ветви в коде.

Замечание. Критерии тестирования могут быть различными. Например, цель может состоять не в обходе всех ветвей, а в задействовании 75% операторов в программе. Все зависит, от метрики тестирования - критерия, согласно которому можно делать вывод об эффективности теста (обнаружение большого числа ошибок).[PP1] 

·  Функциональное тестирование- проверяем функциональность системы, но нас не интересуют такие критерии, как производительность и частота отказов. Возникает понятие доменного тестирования. (x, y,z)- тройка переменных, соответственно типов T1,T2,T3. Образовано пространство тестирования. В общем случае это небесконечномерное пространство, так как каждый из типов ограничен (это ограничение и есть домен), поэтому имеем куб в n-мерном пространстве. Фактически, ограничения типов разбивают области на подобласти. Задача тестирования может быть поставлена так: побывать в каждой области хотя бы один раз. Проблемы возникают в точках, лежащих на границах областей. Мы тестируем функцию, исходя из ее области определения. Это и есть доменный подход к тестированию

·  Структурное тестирование - строим тесты, не для того, чтоб проверить действия операторов, а для проверки связей между большими частями системы(связи между модулями)

Виды тестирования:

·  Сценарное

·  Стрессовое(систему испытывают тем, что заставляют работать ее на пределе возможностей, обрабатывая большое количество информации или воспроизводя критические ситуации или даже одновременно несколько критических ситуаций)

·  Модульное (тестируют взаимосвязи между модулиями, изолируя модули от их реального окружения системы)

·  Интеграционное (тестирование, после того как система установлена на конкретную платформукогда в соответствии с некоторым планом[1], модули объединяются в подсистемы, каждая из которых тестируется отдельно, вне ее реального окружения)

·  Временное (тестируются характеристики, связанные со временем поступления входных данных, наступления внешних и внутренних событий, временем отклика; важно для систем реального времени)

·  Производительности (тестируются характеристики, связанные со скоростью обработки данных, пропускной способностью, количеством одновременно обслуживаемых запросов)

Разновидности объектов тестирования:

·  API –(application program interface)

·  Телекоммуникационный протокол

Например

Клиент Сервер

Программы взаимодействуют через протокол. Но я тестирую в таком случае больше, чем протокол, но мне мало при этом тестировать каждый компонент по отдельности. В таких случаях производится loop back – тестирование.

Машина протокол

Loop back

Тестирование на одной машине. Запуск теста на ней, через протокол результат опять на той же машине. Нагрузка ложится на тестирование именно протокола.

·  Системы реального времени

·  GUI – можно тестировать как самостоятельный вид, а можно свести к API

·  СУБД

SQL API – проверка обращений к серверу, правильно ли отвечает сервер

запросы

Специфика: сложность почти всегда не в интерфейсных функциях, а в структурах данных.

·  Базы Данных - вопрос тестирования заполненной БД неисследован

·  Компиляторы

Программа Компилятор Exe –файл

Что тестировать? Что исполняемая программа работает согласно ожиданиям, или компилятор тестировать?

Компиляторы, условно состоят из 3 больших подчастей

 

front end оптимизаторы кодогенераторы

на входе входов столько, сколько

разобранный оптимизаторов

текст (В gcc- до 70 входов)

задача тестирования более менее ясна для этих

частей

Сложности: описание языка программирования – десятки страниц сложного серьезного текста.

·  Тестирование характеристик качества

Тестирование при отладке: показать, что ошибка в ПО есть. Уже задача разработчика – найти и устранить ошибку по тестам.

Фазы сдачи системы:

·  Настоящая предсдача

·  Настоящая сдача

·  Модификация системы, влечет новое тестирование(регрессионное тестировние)

Partition Analysis (Доменный анализ)

Пространство данных разбивается на области – классы эквивалентности тестовых данных.

Возникает задача фильтрации/селекции - отбирать интересные тестовые наборы и пропускать их через целевую систему.

И, наконец, задача генерации множества данных(тестов).

При помощи тестирования нельзя найти все ошибки.

Кредо: Тесты должны строиться на систематической основе.

Плохих тестов в жизни не бывает, если, конечно, специально не придумывать заранее бесполезные.

Такой подход позволяет :

1.  Оценить качество тестов

2.  Наращивать тесты

3.  Передавать процесс другим людям. Им легче будет вникать в процесс тестирования.

Даже тесты, написанные талантливыми программистами, но без системного подхода, окажутся неудачными.

Идея: Давайте тестировать ФС системы, а не ее реализацию!!!

1.  Трудоемко тестировать реализацию. Гораздо проще тестировать ФС, так как она меньше.

2.  От ФС ожидаем описание полной функциональности системы и надеемся увидеть меньше ошибок, чем при тестировании реализации(это не всегда так).

value f: Nat><NatàBool

f(a, b) is

if (a > b) then true

elseif 2*a > b then true

else false

end

Классы эквивалентности 1) a > b

2 * a > b

~(a > b) /\ ~(2 * a > b)

– Здесь имеем пересечение классов эквивалентности, чего допускать нельзя, так как не знаем, какой случай сработает в общей области.

2) a > b 1-ая область

~(a > b) /\ (2 * a > b) 2-ая область

~(a > b) /\ ~(2 * a > b) 3-яя область

b

3-яя max

 

2-ая

1-ая

 

a

0

Замечание. Если существуют некие ограничения на a и b, то конфликты возникают в окрестностях граничных точек ( например, (0,0) ; (max a, max b))

Размер окрестности для проверки выбирается эмпирически.

Когда мы сгенерировали большое количество тестов, мы должны были сначала задуматься, а разумно мы это сделали? Можно предложить масштабируемость. Например, в окрестностях граничных точек сделать сетку с более мелким шагом, в противном случае выбрать шаг побольше. Это повысит эффективность создания тестов.

Пример.

if c1 \/ c2 then

elseif c3 /\ c4 then

else

end

Классы эквивалентности

1 c1 \/ c2

2  ~(c1 \/ c2) /\ (c3 /\ c4)

3  ~(c1 \/ c2) /\ ~(c3 /\ c4)

Разделим первую область на подклассы: c1 , ~c1 /\ c2

Запишем FDNF для с1 \/ c2

с1 \/ c2 is c1 /\ c2 \/ (~c1 /\ c2) \/ (c1 /\ ~c2)

этого класса нет в подразбиении области 1!!!!

Он пропал? Фактически, разработчик допускал, что такая ситуация может быть невычислима.

Замечание. В RSL действует правило “короткой логики”

Пример. (i isin S) /\ (m(i)=2) если с1=true, то с2 – я не имею права вычислять. с1 с2

[1] Такой план называют «стратегией» тестирования.

 [PP1] - надо бы переформулировать эту фразу.