[ Тематический план ] [ Тестирование программного обеспечения в процессе внедрения и эксплуатации ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ]


                                                                  

Системное тестирование

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

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

       Системное тестирование производится над проектом в целом с помощью метода «черного ящика». Структура программы не имеет никакого значения, для проверки доступны только входы и выходы, видимые пользователю. Тестированию подлежат коды и пользовательская документация.

       Категории тестов системного тестирования:

1. Полнота решения функциональных задач.

2. Стрессовое тестирование — на предельных объемах  нагрузки входного потока.

3. Корректность использования ресурсов (утечка памяти, возврат ресурсов).

4. Оценка производительности.

5. Эффективность защиты от искажения данных и  некорректных действий.

6. Проверка инсталляции и конфигурации на разных  платформах.

7. Корректность документации.

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

 Эффективность и оптимизация программ

       Эффективными считаются программы, требующие  минимального времени выполнения и/или минимального объема оперативной памяти. Особые требования к эффективности 

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

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

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

      И тем более не следует «платить» за увеличение  эффективности снижением технологичности разрабатываемого  программного обеспечения. Исключения возможны лишь при очень жестких требованиях и наличии соответствующего контроля за качеством.

      Частично проблему эффективности программ решают за программиста компиляторы.

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

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

машинно-независимые выполняют оптимизацию на уровне входного языка, например, вынесение вычислений  константных (независящих от индекса цикла) выражений изциклов и т. п. 

       Естественно, нельзя вмешаться в работу компилятора, но  существует много возможностей оптимизации программы на  уровне команд.  

       Способы экономии памяти. Принятие мер по экономии  памяти предполагает, что в каких-то случаях эта память неэкономно использовалась. Учитывая, что анализировать имеет смысл  только операции размещения данных, существенно влияющие на  характеристику эффективности, следует обращать особое  внимание на выделение памяти под данные структурных типов  (массивов, записей, объектов и т. п.).

       Прежде всего при наличии ограничений на использование памяти следует выбирать алгоритмы обработки, не требующие дублирования исходных данных структурных типов в процессе обработки. Примером могут служить алгоритмы сортировки  массивов, выполняющие операцию в заданном массиве, например хорошо известная сортировка методом «пузырька».

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

      Также следует помнить, что при передаче структурных  данных в подпрограмму «по значению» копии этих данных  размещаются в стеке. Избежать копирования иногда удается, если  передавать данные «по ссылке», но как неизменяемые (описанные const). В последнем случае в стеке размещается только адрес данных, например:

Type Mas.4iv array [I. 100] of real;

function Summa (Const a:Massiv; .)  

         Способы уменьшения времени выполнения. Как уже  упоминалось выше, для уменьшения времени выполнения в первую  очередь необходимо анализировать циклические участки программы с большим количеством повторений. При их написании  необходимо по возможности:

         выносить вычисление константных, т. е. не зависящих от параметров цикла, выражений из циклов;

         избегать «длинных» операций умножения и деления,  заменяя их сложением, вычитанием и сдвигами;

         минимизировать преобразования типов в выражениях;

         оптимизировать запись условных выражений — исключать лишние проверки;

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

         избегать использования различных типов в выражении и т. п.

Рассмотрим следующие примеры.

Пример 5.3. Пусть имеется цикл следующей структуры (Pascal):

for у: 0 to 99 do

for x: 0 to 99 do

а [320*х+у] S [k,l];  

      В этом цикле операции умножения и обращения к элементу S[k] выполняются 10 000 раз.

Оптимизируем цикл, используя, что 320 = 28 + 26:

ski: =S [k,l]; {выносим обращение к элементу массива из цикла}

for x: 0 to 99 do (меняем циклы местами}

begin

х shl 8 + х shl 6; {умножение заменяем на сдвиги и выносим из цикла)

for у; 0 to 99 do

a [i+y] =skl;

end;

В результате вместо 10 000 операций умножения будут  выполняться 200 операций сдвига, а их время приблизительно сравнимо со временем выполнения операции сложения. 

Обращение к элементу массива S[k] будет выполнено 1 раз.

Пример 5.4. Пусть имеется цикл, в теле которого  реализовано сложное условие:

for k: 2 to n do

begin

ifx[k] > ук then S: S+y[k]-x [к];

if (x [k]< = yk) and (y[k]<yk) then S: = S+yk-x[k];

end;... 

В этом цикле можно убрать лишние проверки:

for k: =2 to n do

begin

ifx [k]>yk then S:=S+y[к]-х[к]

else

ify[k]<yk then S: =S+yk-x [k];

end;

       Обратите внимание на то, что в примере 5.3 понять, что  делает программа, стало сложнее, а в примере 5.4 — практически нет. Следовательно, оптимизация, выполненная в первом  случае, может ухудшить технологичность программы, а потому не очень желательна.