Система тестирования. Часть 6

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

  • Вопросы из файла достаются в случайном порядке
  • Тестируемый может перескакивать через вопросы и возвращаться назад
  • Правильных ответов может быть несколько
  • Варианты ответа к вопросу также выстраиваются случайно
  • Имя файла можно передать как параметр командной строки, и программа должна его открыть
  • Возможность начать тестирование сначала без перезапуска программы

Алгоритм проверки

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

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

Для того, что бы лучше понять, давайте рассмотрим пример. Пусть есть вопрос с шестью вариантами ответа, и три варианта правильные. Рассмотрим первый вариант: тестируемый отметил все правильные ответы – в таком случае за вопрос он получит 100%. Другой вариант: отметил только два варианта ответа из трех. Тогда получается, что вопрос верен лишь на 66%. Если же выбрать, к примеру, два правильных ответа и один неправильный, то 33%.

Подобный алгоритм с процентами является все же более справедливым, нежели просто «верно» или «неверно».

Расширенная работа с данными

Прежде чем приступить к непосредственно разработке программы-теста, нужно сначала расширить классы, созданные нами во второй части цикла статей (ссылка). То есть  классы, описывающие данные из xml файла.

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

Добавим в папку lib еще один unit – StDataTest. Начнем с класса варианта ответа: TStAnswer. Единственное, что нам нужно добавить этому классу – это поле Selected, определяющее, выбран ли вариант ответа или нет.

Класс наследник назовем TStAnswerTest.

  1. unit StDataTest;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  7.   xmldom, XMLIntf, msxmldom, XMLDoc, StdCtrls, Contnrs, StData;
  8.  
  9. type
  10.   TStAnswerTest = class(TStAnswer)
  11.   public
  12.     Selected: boolean;
  13.     constructor Create;
  14.   end;
  15.  
  16. implementation
  17.  
  18. constructor TStAnswerTest.Create;
  19. begin
  20.   inherited Create;
  21.   Selected := false;
  22. end;

Теперь нам нужно расширить класс вопроса: TStQuestion. Ему нужно лишь добавить метод Check, который будет проверять выбранные варианты ответа согласно алгоритму, описанному выше.

  1. TStQuestionTest = class(TStQuestion)
  2. public
  3.   function Check: Integer;
  4. end;
  5.  
  6. ...
  7.  
  8. function TStQuestionTest.Check: Integer;
  9. var
  10.   i: integer;
  11.   Right: integer; // Количество верных ответов
  12.                   // За вычетом неправильных
  13.   AllRight: integer; // Количество верных ответов в вопросе
  14.   tmpAnswer: TStAnswerTest;
  15. begin
  16.   Right := 0;
  17.   AllRight := 0;
  18.   // Цикл по всем варинатам ответа
  19.   for i := 0 to StAnswerList.Count - 1 do
  20.     begin
  21.       tmpAnswer = TStAnswerTest( StAnswerList.Items[i] );
  22.       // Если текущий вариант ответа правильный
  23.       // Увеличиваем AllRight
  24.       if tmpAnswer.Right then
  25.         Inc( AllRight );
  26.      
  27.       // Если варинт ответа выбран и он верный
  28.       if ( tmpAnswer.Right =
  29.            tmpAnswer.Selected ) and
  30.          ( tmpAnswer.Right ) then
  31.         Inc( Right ) // Увеличиваем Right
  32.       else if ( tmpAnswer.Selected ) and
  33.               ( not tmpAnswer.Right ) then
  34.         Dec( Right ); // Если же выбран неверный варинт ответа
  35.                       // То уменьшаем
  36.  
  37.     end;
  38.   if Right < 0 then // Если неправильных ответов больше
  39.     Result := 0 // То сбрасываем в ноль
  40.   else // Считаем общий процент
  41.     Result := Round ( ( Right / AllRight ) * 100 );
  42. end;

Представление варианта ответа

Теперь перейдем к представлению варианта ответа непосредственно в программе. Несмотря на то, что пока никакой программы теста еще даже и нет, с этим нужно разобраться. Создадим в папке Test юнит StAnswerBlock. В нем и будет блок представления варианта ответа.

Возможным вариантом является следующий:

Не отмеченный: 

Отмеченный тестируемым: 

За представление варианта ответа будет отвечать класс TStAnswerBlock, наследованный от TPanel. Рассмотрим картинку блока, так сказать, в разрезе:

 

  • TextLabel– метка, куда выводится текст ответа
  • CImage – изображение, видимое, если вариант ответа выбран.

Перечисленные выше компоненты сделаем скрытыми полями класса. Кроме того, добавим к этому классу следующие члены:

  • Поле Answer: TStAnswer. Сам вариант ответа
  • Свойство Selected: boolean. Доступное для чтения и записи свойство, определяющее отмечен ли тестируемым вариант ответа.
  • Свойство AText: string. Текст варианта ответа.

В итоге, объявление класса выглядит так:

  1. TStAnswerBlock = class(TPanel)
  2. private
  3.   FSelected: boolean;
  4.   TextLabel: TLabel;
  5.   CImage: TImage;
  6.   function GetAText: string;
  7.   procedure SetAText(const Value: string);
  8.   procedure SetSelected(const Value: boolean);
  9. public
  10.   Answer: TStAnswer;
  11.   constructor Create(setAnswer: TStAnswer; AOwner: TComponent; setPicture:
  12.       TPicture);
  13.   destructor Destroy; override;
  14.   procedure Click(Sender: TObject);
  15.   property AText: string read GetAText write SetAText;
  16.   property Selected: boolean read FSelected write SetSelected;
  17. end;

Рассмотрим методы этого класса.

Конструктор

Конструктор принимает три параметра: AOwner, setAnswer – вариант ответа, который будет привязан к этому блоку и setPicture – картинка для поля CImage.

  1. constructor TStAnswerBlock.Create(setAnswer: TStAnswer; AOwner: TComponent;
  2.     setPicture: TPicture);
  3. begin
  4.   inherited Create( AOwner );
  5.   Parent := ( TWinControl (AOwner));
  6.   Width := 150;
  7.   Height := 24;
  8.   Color := clWhite;
  9.   ParentBackground := false;
  10.   BevelOuter := bvNone;
  11.   Ctl3D := false;
  12.   BorderStyle := bsSingle;
  13.   ShowCaption := false;
  14.   OnClick := Click;
  15.  
  16.   CImage := TImage.Create( self );
  17.   CImage.Parent := self;
  18.   CImage.Width := 16;
  19.   CImage.Height := 16;
  20.   CImage.Left := 4;
  21.   CImage.Top := 3;
  22.   CImage.Picture := setPicture;
  23.   CImage.OnClick := Click;
  24.  
  25.   TextLabel := TLabel.Create( self );
  26.   TextLabel.Parent := self;
  27.   TextLabel.Top := 4;
  28.   TextLabel.OnClick := Click;
  29.  
  30.   Answer := setAnswer;
  31.   AText := setAnswer.Text;
  32.   Selected := TStAnswerTest( Answer ).Selected;
  33. end;

Клик по блоку

Метод Click. Обработчик события OnClick по блоку. Задается свойство Selected. То есть, если вариант ответа выбран, то снимаем выбор, или наоборот, если не выбран, то делаем выбранным.

  1. procedure TStAnswerBlock.Click(Sender: TObject);
  2. begin
  3.   Selected := not Selected;
  4. end;

Метод SetSelected

Метод при записи в свойство Selected. В зависимости от значения либо показывает либо убирает картинку.

  1. procedure TStAnswerBlock.SetSelected(const Value: boolean);
  2. begin
  3.   CImage.Visible := Value;
  4.   ( TStAnswerTest (Answer) ).Selected := Value;
  5.   if Value then
  6.     begin
  7.       TextLabel.Left := 25;
  8.       Width := TextLabel.Width + 33;
  9.     end
  10.   else
  11.     begin
  12.       TextLabel.Left := 5;
  13.       Width := TextLabel.Width + 12;
  14.     end;
  15.  
  16.   FSelected := Value;
  17. end;

Методы SetAText и GetAText

Методы для чтения и записи в свойство AText. Значения записывается и получается из свойства Caption метки TextLabel.

  1. function TStAnswerBlock.GetAText: string;
  2. begin
  3.   Result := TextLabel.Caption;
  4. end;
  5.  
  6. procedure TStAnswerBlock.SetAText(const Value: string);
  7. begin
  8.   TextLabel.Caption := Value;
  9. end;

На это подготовительная часть к разработке программы-теста завершена. Далее мы перейдем непосредственно к созданию самой программы.

delphi, тест

Комментарии (0)

Добавить комментарий

  • Допустимые html-теги:
    <b> </b> - жирный шрифт
    <em> </em> - наклонный
    <s> </s> - зачеркнутый
    <pre> </pre> - сохранение отступов (печать кода)
    [?]
Введите текст с картинки