пятница, 15 ноября 2013 г.

В думах о тестировании

Я уже писал о контрольных точках - http://18delphi.blogspot.ru/2013/04/blog-post_6244.html

Я достаточно широко применяю этот подход.

Я внедряю тестовый код в реальные проектные файлы.


Позволю привести себе пример:
unit Script.Parser;

interface

uses
 Classes,
 CoreObject
 ;

{$IfNDef NoTesting}
 {$Define TestParser}
{$EndIf  NoTesting}

type
 TscriptParser = class(TCoreObject)
  private
   f_Stream : TStream;
   {$IfDef TestParser}
   f_GetCharLog : TStream;
   // - сразу думаем о тестировании, в этот поток будем выводить
   //   результат работы функции GetChar
   f_ReadLnLog : TStream;
   // - сразу думаем о тестировании, в этот поток будем выводить
   //   результат работы функции ReadLn
   {$EndIf TestParser}
   f_EOF : Boolean;
   f_CurrentLine : String;
  protected
   procedure Cleanup; override;
   function ReadLn: String;
  protected
   function GetChar(out aChar: AnsiChar): Boolean;
  public
   constructor Create(const aStream : TStream; const aFileName : String); overload;
   constructor Create(const aFileName : String); overload;
   function EOF: Boolean;
   procedure NextToken;
 end;//TscriptParser

implementation

uses
 System.SysUtils
 ;

constructor TscriptParser.Create(const aStream : TStream; const aFileName : String);
begin
 inherited Create;
 f_EOF := false;
 f_Stream := aStream;
 {$IfDef TestParser}
 f_GetCharLog := TFileStream.Create(aFileName + '.GetChar.log', fmCreate);
 f_ReadLnLog := TFileStream.Create(aFileName + '.ReadLn.log', fmCreate);
 {$EndIf TestParser}
end;

constructor TscriptParser.Create(const aFileName : String);
var
 l_FileName : String;
begin
 l_FileName := ExtractFilePath(ParamStr(0)) + '\' + aFileName;
 Create(TFileStream.Create(l_FileName, fmOpenRead), l_FileName);
end;

procedure TscriptParser.Cleanup;
begin
 FreeAndNil(f_Stream);
 {$IfDef TestParser}
 FreeAndNil(f_GetCharLog);
 FreeAndNil(f_ReadLnLog);
 {$EndIf TestParser}
 inherited;
end;

function TscriptParser.GetChar(out aChar: AnsiChar): Boolean;
begin
 if (f_Stream.Read(aChar, SizeOf(aChar)) = SizeOf(aChar)) then
 begin
  Result := true;
  {$IfDef TestParser}
  f_GetCharLog.Write(aChar, SizeOf(aChar));
  {$EndIf TestParser}
 end
 else
  Result := false;
end;

function TscriptParser.ReadLn: String;
{$IfDef TestParser}
const
 cEOL : AnsiString = #13#10;
var
 l_Result : AnsiString;
{$EndIf TestParser}
var
 l_Char : AnsiChar;
 l_Line : String;
begin
 {$IfDef TestParser}
 try
 {$EndIf TestParser}
  l_Line := '';
  while GetChar(l_Char) do
  begin
   if (l_Char = #13) then
   begin
    if GetChar(l_Char) then
    begin
     if (l_Char = #10) then
     begin
      Result := l_Line;
      Exit;
     end//l_Char = #10
     else
      Assert(false, 'Что-то пошло не так, после символа 13 нет символа 10');
    end//GetChar(l_Char)
    else
     Assert(false, 'Что-то пошло не так, после символа 13 сразу конец файла');
   end;//l_Char = #13
   l_Line := l_Line + l_Char;
  end;//while GetChar(l_Char)
  f_EOF := true;
  Result := l_Line;
 {$IfDef TestParser}
 finally
  l_Result := Result;
  f_ReadLnLog.Write(@l_Result[1], Length(l_Result));
  f_ReadLnLog.Write(@cEOL[1], Length(cEOL));
 end;//try..finally
 {$EndIf TestParser}
end;

procedure TscriptParser.NextToken;
begin
 while(f_CurrentLine = '') do
 begin
  f_CurrentLine := ReadLn;
  if (f_CurrentLine = '') then
   if f_EOF then
    Exit;
 end;//while(f_NextToken = '')
 f_CurrentLine := '';
end;

function TscriptParser.EOF: Boolean;
begin
 Result := f_EOF;
end;

end.

Тут в класс TscriptParser "внедрено" две "контрольных точки". f_ReadLnLog и f_GetCharLog.

Я такие "контрольные точки" обычно делаю "не руками", а на уровне модели, но всё равно - надеюсь, что идея понятна.

При этом получившиеся "эталоны" я обычно кладу в CVS/SVN и контролирую их разницу. На разных тестовых наборах. Про это я напишу чуть позже.

Пример доступен тут - https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/GUITests/Chapter0/

Комментариев нет:

Отправить комментарий