Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
методическое пособие для самостоятельных работ.doc
Скачиваний:
21
Добавлен:
16.11.2019
Размер:
2.89 Mб
Скачать

6.3. Составление тестов

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

  • события без исходных данных;

  • события с исходными данными.

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

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

  1. Существуют ли обращения к переменным, не имеющим значения? Если Вы пользуетесь значениями по умолчанию, убедитесь, что они соответствуют требуемым; лучше присвоить все исходные значения явно.

  2. Находятся ли индексы в допустимых границах? Не забудьте, что в массивах на Pascal’e и в объекте StringGrid разная очередность индексов.

  3. Находятся ли значения переменных в допустимых для данного типа данных границах? Это касается и промежуточных результатов.

  4. Имеются ли вычисления с нечисловыми данными? Нет ли возможности потенциальной ошибки в преобразованиях типов данных? Процедура val( ); функции StrToInt, StrToFloat. . . Согласуются ли типы числовых данных при вычислениях (тип результата деления целых не всегда целый).

  5. Возможны ли недопустимые значения аргументов функций (например, логарифм отрицательного числа), деление на нуль, квадратный корень отрицательного числа?

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

  7. Правильно ли расставлены пары Begin. . .End; некоторые программисты рекомендуют: если набрали Begin, наберите сразу же и End и вставьте нужные операторы между ними. Не забудьте, когда за End нужна ; а когда нет!

  8. Существует ли потенциальная возможность зацикливания в циклах while . . do и repeat . . until, изменяется ли условие продолжения (прерывания) цикла по ходу его выполнения?

  9. Соответствуют ли формальные и фактические параметры по количеству, типу и очередности?

  10. Если решается задача, имеющая физическую суть, согласованы ли единицы измерения?

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

Имеется два подхода к тестированию:

  • функциональное тестирование (тестирование по данным, тестирование “черного ящика”);

  • структурное тестирование (тестирование по управлению, тестирование “белого ящика”).

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

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

Наиболее распространенным методом функционального тестирования является эквивалентное разбиение: всю область значений исходных данных разбивают на конечное число классов эквивалентности так, чтобы можно было предположить, что каждый тест, являющийся представителем некоторого класса, эквивалентен любому другому тесту этого класса. Например, исходя из определения модуля y=abs(x), можем выделить два класса эквивалентности: x>=0 и x<0. Различают:

  • правильные классы эквивалентности, представляющие допустимые значения исходных данных;

  • неправильные классы эквивалентности, представляющие недопустимые значения исходных данных.

В примере с модулем неправильным классом эквивалентности является случай, когда вместо х используется нечисловое значение. Для y=ln(x) правильным классом эквивалентности является x>0; неправильными классами x<=0 и нечисловое значение х. Программа должна быть составлена таким образом, чтобы исходные данные из неправильных классов эквивалентности не вызывали программные прерывания во время решения, а лишь сообщение об этом, запрашивание новых исходных данных или возврат в исходное состояние. Например, в системе продажи билетов, если кассир ошибочно ввел дату 30.02, то необходимо запрашивать новую и продолжать работу. Чтобы это обеспечить, программа должна быть протестирована на всех правильных и неправильных классах эквивалентности.

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

if (a>1)AND(b=0) then x:=x/a;

if (a=2)OR(x>1) then x:=x+1;

Нужны тесты, удовлетворяющие следующим условиям:

1.

a>1 b=0

5.

a=2 x>1

2.

a>1 b<>0

6.

a=2 x<=1

3.

a<=1 b=0

7.

a<>2 x>1

4.

a<=1 b<>0

8.

a<>2 x<=1

Для того, чтобы протестировать эти комбинации, достаточно 4 теста:

  • x=4 a=2 b=0 покрывает 1, 5;

  • x=1 a=2 b=1 покрывает 2, 6;

  • x=2 a=0 b=0 покрывает 3, 7;

  • x=1 a=0 b=1 покрывает 4, 8.

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

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

  • файл пуст, в массиве нет элементов;

  • требуется найти максимальный элемент, но все элементы равны; требуется выделить n наибольших элементов, но n-1, n и n+1 элементы имеют одинаковые значения (после упорядочения).

Контрольные вопросы

  1. Что такое синтаксическая ошибка? Возможности их устранения.

  2. Поясните понятия: тестирование, отладка, испытание программы.

  3. Поясните понятие трассировка.

  4. Для чего используются окна просмотра?

  5. Что такое точки прерываний?

  6. Поясните понятие функциональне тестирование.

  7. Поясните понятие структурное тестирование.

  8. Что такое эквивалентное разбиение?

  9. Какие классы эквивалентного разбиения вы знаете?

Примеры составления программ

  1. Иллюстрация пробной версии программы

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls;

type

TForm1 = class(TForm)

Label1: TLabel;

Label2: TLabel;

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

end.

  1. Тестовая программа

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, Buttons, ExtCtrls;

type

TfmExample = class(TForm)

Panel1: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edInput: TEdit;

lbOutput: TLabel;

mmOutput: TMemo;

private

{ Private declarations }

public

{ Public declarations }

end;

var

fmExample: TfmExample;

implementation

{$R *.dfm}

end.

  1. Иллюстрация процедурных типов

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, Buttons, ExtCtrls;

type

TfmExample = class(TForm)

Panel1: TPanel;

bbRun: TBitBtn;

bbCancel: TBitBtn;

edInput: TEdit;

mmOutput: TMemo;

lbOutput: TLabel;

procedure bbRunClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

fmExample: TfmExample;

implementation

{$R *.dfm}

Function Sin1(X: Real): Real;

begin

Result := (Sin(X) + 1) * Exp(-X)

end; // Sin1

Function Cos1(X: Real): Real;

begin

Result := (Cos(X) + 1) * Exp(-X)

end; // Cos1

procedure TfmExample.bbRunClick(Sender: TObject);

type

Func = function (X: Real): Real; // Процедурный тип

Procedure PrintFunc(NP: Integer; F: Func) ;

var

k: Integer;

X: Real;

begin

for k := 0 to NP do

begin

X := k * 2 * pi / NP;

mmOutput.Lines.Add(FloatToStrF(X, ffExponent, 10, 2) +

#9 + #9 + FloatToStrF(F(X), ffExponent, 10, 2));

end;

end; // PrintFunc

begin // bbRunClick

mmOutput.Lines.Add(#9'Функция SIN1:');

PrintFunc(10, Sin1);

mmOutput.Lines.Add(#9'Функция COS1:');

PrintFunc(10, Cos1);

end;

end.

  1. Отображение свойств объекта

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, Buttons, ExtCtrls;

type

TfmExample = class(TForm)

Panel1: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edInput: TEdit;

lbOutput: TLabel;

mmOutput: TMemo;

procedure bbRunClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

fmExample: TfmExample;

implementation

{$R *.DFM}

procedure TfmExample.bbRunClick(Sender: TObject);

var

MemSourceStream, MemDestStream: TMemoryStream;

begin

MemSourceStream := TMemoryStream.Create;

try

MemDestStream := TMemoryStream.Create;

try

MemSourceStream.WriteComponent(bbRun);

MemSourceStream.Seek(0, soFromBeginning);

ObjectBinaryToText(MemSourceStream, MemDestStream);

MemDestStream.Seek(0, soFromBeginning);

mmOutput.Lines.LoadFromStream(MemDestStream)

finally

MemDestStream.Free

end;

finally

MemSourceStream.Free

end;

end;

end.

  1. Демонстрация отображения файла в память

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, ComCtrls, Spin;

type

TForm1 = class(TForm)

btMem: TButton;

btFile: TButton;

se: TSpinEdit;

Label1: TLabel;

pb: TProgressBar;

Label2: TLabel;

lbMem: TLabel;

lbFile: TLabel;

procedure btMemClick(Sender: TObject);

procedure btFileClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.btMemClick(Sender: TObject);

// Создание файла методом его отображения

type

PReal = ^Real;

var

HFile, HMap: THandle;

AdrBase, AdrReal: PReal;

k: Integer;

FSize: Cardinal;

BegTime: TDateTime;

begin

BegTime := Time; // Засекаем время пуска

// Готовим ProgressBar:

pb.Max := se.Value;

pb.Position := 0;

pb.Show;

FSize := se.Value * SizeOf(Real); // Длина файла

HFile := FileCreate('test.dat'); // Создаем файл

if HFile = 0 then // Ошибка: возбуждаем исключение

raise Exception.Create('Ошибка создания файла');

try

// Отображаем файл в память

HMap := CreateFileMapping(HFile, NIL, PAGE_READWRITE, 0, FSize, NIL);

if HMap = 0 then // Ошибка: возбуждаем исключение

raise Exception.Create('Ошибка отображения файла');

try

// Создаем окно просмотра:

AdrBase := MapViewOfFile(HMap, FILE_MAP_WRITE, 0, 0, FSize);

if AdrBase = NIL then // Ошибка: возбуждаем исключение

raise Exception.Create('Невозможно просмотреть файл');

// Сохраняем начальный адрес для правильной ликвидации

// окна просмотра:

AdrReal := AdrBase;

for k := 1 to se.Value do

begin

AdrReal^ := Random; // Помещаем в файл новое число

// Перед наращиванием текущего адреса необходимо

// привести его к типу Integer или Cardinal:

AdrReal := Pointer(Integer(AdrReal) + SizeOf(Real));

lbMem.Caption := IntToStr(k);

pb.Position := k;

Application.ProcessMessages;

end;

// Освобождаем окно просмотра:

UnmapViewOfFile(AdrBase)

finally

// Освобождаем отображение

CloseHandle(HMap)

end

finally

// Закрываем файл

CloseHandle(HFile)

end;

// Сообщаем время счета

pb.Hide;

lbMem.Caption := TimeToStr(Time-BegTime)

end;

procedure TForm1.btFileClick(Sender: TObject);

// Создание файла обычным методом

var

F: File of Real;

k: Integer;

BegTime: TDateTime;

R: Real; // Буферная переменная для обращение к Write

begin

BegTime := Time; // Засекаем начальное время счета

// Готовим ProgressBar:

pb.Max := se.Value;

pb.Position := 0;

pb.Show;

// Создаем файл:

AssignFile(F, 'test.dat');

Rewrite(F);

for k := 1 to se.Value do

begin

R := Random; // Параметрами обращения к Write

Write(F, R); // могут быть только переменные

lbFile.Caption := IntToStr(k);

pb.Position := k;

Application.ProcessMessages;

end;

CloseFile(F);

pb.Hide;

lbFile.Caption := TimeToStr(Time-BegTime)

end;

end.

  1. Иллюстрация использования функции CreateFontIndirect

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

TForm1 = class(TForm)

procedure FormPaint(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormPaint(Sender: TObject);

var

X: Integer;

LF: TLogFont;

Fnt: HFont;

const

Text = 'Лучшая в мире система программирования';

begin

// Определяем параметры нового шрифта

FillChar(LF, SizeOf(LF), 0);

with LF do

begin

lfHeight := 20;

lfWeight := fw_Medium;

lfUnderline := 1;

lfEscapement := 450;

StrPCopy(lfFaceName, 'Courier New Cyr');

end;

with Form1.Canvas do

begin

// Создаем шрифт

Fnt := CreateFontIndirect(LF);

// Присваиваем его дескриптор шрифту канвы

Font.Handle := Fnt;

// Выводим текст под углом +45 градусов

TextOut(0, 300, Text);

X := TextWidth(Text);

DeleteObject(Fnt); // Удаляем ненужный шрифт

// Изменяем параметры шрифта

with LF do

begin

lfHeight := 120;

lfEscapement := -900;

lfWeight := fw_Heavy;

StrPCopy(LF.lfFaceName, 'Arial Cyr');

end;

Fnt := CreateFontIndirect(LF); // Создаем новый шрифт

Font.Handle := Fnt;

Font.Color := clRed;

// Выводим с наклоном -90 градусов

TextOut(X+10, 10, 'Delphi');

DeleteObject(Fnt); // Удаляем ненужный шрифт

end;

end;end.

Задания для самостоятельного выполнения

  1. Ввести в компонент Memo1 строку, состоящую из нескольких слов. Составить из данной строки новую строку, в которую войдут только слова, содержащие не более 7 букв. Вывести полученную строку в компонент Label1.

  2. Ввести в компонент Memo1 строку, состоящую из нескольких слов. Поменять местами первое и последнее слова в строке. Вывести новую строку в компонент Label1.

  3. Ввести в компонент Memo1 строку, состоящую из нескольких слов. Составить строку, в которую войдут слова исходной строки имеющие четное количество букв. Вывести новую строку в компонент Label1.

  4. Ввести в компонент Memo1 строку, состоящую из нескольких слов. Заменить в строке слова, содержащие нечетное количество букв, на номер слова. Вывести новую строку в компонент Label1.

  5. Ввести в компонент Memo1 строку, состоящую из нескольких слов. Ввести букву в компонент Edit1. Заменить в исходной строке первую и последнюю букву пятого слова на заданную букву. Вывести новую строку в компонент Label1.

  6. Ввести в компонент Memo1 строку, состоящую из нескольких слов. Ввести букву в компонент Edit1. Заменить в каждом слове строки каждую третью букву на букву, заданную в компоненте Edit1. Вывести новую строку в компонент Label1.

  7. Ввести в компонент Memo1 строку, состоящую из нескольких слов. Заменить все слова, содержащие не менее пяти букв, на слово «пример». Вывести новую строку в компонент Label1.