понедельник, 16 марта 2015 г.

GUI-testing 14. GUI-testing “in the Russian style”. Adding DUnit to our “scripts”

Original in Russian: http://18delphi.blogspot.ru/2013/11/gui-dunit.html

GUI-testing. Table of contents

Now, let us make one thing – add DUnit to our scripts.

First, let’s make test VCL-project in analogy to FM-project.

It is more than simple:

The code of the project:

program VCLProject;
 
uses
  Vcl.Forms,
  Script.Engine in 'Scripting\Script.Engine.pas',
  Script.Parser in 'Scripting\Script.Parser.pas',
  Script.Interfaces in 'Scripting\Script.Interfaces.pas',
  Core.Obj in 'Core\Core.Obj.pas',
  Testing.Engine in 'Testing\Testing.Engine.pas',
  Script.WordsInterfaces in 'Scripting\Script.WordsInterfaces.pas',
  Script.Code in 'Scripting\Script.Code.pas',
  Script.Dictionary in 'Scripting\Script.Dictionary.pas',
  Script.Word in 'Scripting\Script.Word.pas',
  Script.StringWord in 'Scripting\Script.StringWord.pas',
  Script.UnknownToken in 'Scripting\Script.UnknownToken.pas',
  Script.Axiomatics in 'Scripting\Script.Axiomatics.pas',
  Script.Word.Examples in 'Scripting\Script.Word.Examples.pas',
  Script.Word.Buttons in 'Scripting\Script.Word.Buttons.pas',
  VCLForm1 in 'VCLForm1.pas' {Form1},
  GUITestRunner,
  DUnit.Scripting.AutoTests in 'DUnitScripting\DUnit.Scripting.AutoTests.pas',
  DUnit.Scripting.AutoTest in 'DUnitScripting\DUnit.Scripting.AutoTest.pas';
 
{$R *.res}
 
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  //  GUITestRunner.RunRegisteredTests;
  GUITestRunner.RunRegisteredTestsModeless;
  Application.Run;
end.

The code of the main form:

unit VCLForm1;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
 
  Script.Interfaces
  ;
 
type
  TForm1 = class(TForm, IscriptCompileLog, IscriptRunLog)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Edit1: TEdit;
    CompileLog: TMemo;
    RunLog: TMemo;
    Run: TButton;
    procedure Button1Click(Sender: TObject);
    procedure RunClick(Sender: TObject);
  private
    { Private declarations }
   procedure IscriptCompileLog_Log(const aString: String);
   procedure IscriptCompileLog.Log = IscriptCompileLog_Log;
   procedure IscriptRunLog_Log(const aString: String);
   procedure IscriptRunLog.Log = IscriptRunLog_Log;
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
uses
 Testing.Engine,
 Script.Engine
 ;
 
{$R *.dfm}
 
procedure TForm1.IscriptCompileLog_Log(const aString: String);
begin
 CompileLog.Lines.Add(aString);
 {$IfNDef NoTesting}
 TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'IscriptCompileLog_Log')).PutValue(aString);
 {$EndIf  NoTesting}
end;
 
procedure TForm1.IscriptRunLog_Log(const aString: String);
begin
 RunLog.Lines.Add(aString);
 {$IfNDef NoTesting}
 TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'IscriptRunLog_Log')).PutValue(aString);
 {$EndIf  NoTesting}
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
 Edit1.Text := (Sender As TButton).Caption + ' clicked';
end;
 
procedure TForm1.RunClick(Sender: TObject);
const
 l_FileName = 'FirstScript.script';
begin
 CompileLog.Lines.Clear;
 RunLog.Lines.Clear;
 {$IfNDef NoTesting}
 TtestEngine.StartTest(l_FileName);
 try
 {$EndIf  NoTesting}
  TScriptEngine.RunScript(l_FileName, Self, Self);
 {$IfNDef NoTesting}
 finally
  TtestEngine.StopTest;
 end;//try..finally
 {$EndIf  NoTesting}
end;
 
end.

ALL THE REST project files HAVE NOT CHANGED.

The publication of scripts in DUnit is MORE that SIMPLE:

The code of “test suite”:

unit DUnit.Scripting.AutoTests;
 
interface
 
uses
  TestFrameWork,
  Core.Obj
  ;
 
type
 TautoTests = class(TTestSuite)
  public
   procedure AddTests(testClass: TTestCaseClass); override;
 end;//TautoTests
 
implementation
 
uses
  DUnit.Scripting.AutoTest,
  System.IOUtils,
  System.SysUtils,
  Testing.Engine
  ;
 
procedure TautoTests.AddTests(testClass: TTestCaseClass);
var
 l_FileName : String;
begin
 Assert(testClass.InheritsFrom(TautoTest));
 {$IfNDef NoTesting}
 TtestEngine.StartTest('Initialization');
 try
 {$EndIf  NoTesting}
  for l_FileName in TDirectory.GetFiles(ExtractFilePath(ParamStr(0)), '*.script') do
  begin
   {$IfNDef NoTesting}
   TtestEngine.CurrentTest.SocketMetric(TtestSocket.Create(Self, 'AddTests')).PutValue(l_FileName);
   {$EndIf  NoTesting}
   AddTest(RautoTest(testClass).Create(l_FileName));
//   TScriptEngine.RunScript(l_FileName, Self, Self);
  end;//for l_FileName in TDirectory
 {$IfNDef NoTesting}
 finally
  TtestEngine.StopTest;
 end;//try..finally
 {$EndIf  NoTesting}
 //inherited;
end;
 
initialization
 TestFramework.RegisterTest(TautoTests.Create(TautoTest));
 
end.

Here the code for l_FileName in TDirectory.GetFiles – flips through the files in the directory by mask.

My gratitude to Roman Yankovsky (http://roman.yankovsky.me/) for a hint (http://programmingmindstream.blogspot.ru/2013/12/blog-post_16.html?showComment=1387279738393#c6313390821290709952).

And the type of a SEPARATE script test:

unit DUnit.Scripting.AutoTest;
 
interface
 
uses
  TestFrameWork
  ;
 
type
 TautoTest = class(TTestCase)
   public
    constructor Create(MethodName: string); override;
   protected
     procedure DoIt;
 end;//TautoTest
 
 RautoTest = class of TautoTest;
 
implementation
 
uses
  Script.Engine,
  Testing.Engine
  ;
 
constructor TautoTest.Create(MethodName: string);
begin
 inherited Create(MethodName);
 FMethod := DoIt;
end;
 
procedure TautoTest.DoIt;
begin
 {$IfNDef NoTesting}
 TtestEngine.StartTest(FTestName);
 try
 {$EndIf  NoTesting}
  TScriptEngine.RunScript(FTestName, nil, nil);
 {$IfNDef NoTesting}
 finally
  TtestEngine.StopTest;
 end;//try..finally
 {$EndIf  NoTesting}
// TScriptEngine.RunScript(FTestName, nil, nil);
end;
 
end.

What is it all about?

OBVIOUSLY – we flip through the files by mask *.script in the directory of EXE-file and create a SEPARATE DUnit-test for EACH file of this kind.

We launch our project and see two forms:

The main form of the application:



And the form with the list of tests:


And one more remark. Of course, in a SIMILAR WAY you can “multiply” tests on EVERY iterator parameter, not just on script file.

Later I’m planning to write how tests can be “multiplied” on input/output.

This is something like in library I’ve been referred to by Roman Yankovsky, where tests multiply on their own attributes. Here - http://18delphi.blogspot.ru/2013/04/blog-post_7108.html . - the references about it are collected.

In the “script axiomatics” one ASSUMPTION is made.

Right here:

procedure TkwFindComponent.DoDoIt(aContext: TscriptContext);
var
 l_Name : String;
 l_Component : TComponent;
 l_ActiveForm : TForm;
 l_Index : Integer;
begin
 l_Name := aContext.PopString;
 Assert(l_Name <> '');
 l_ActiveForm := nil;
 for l_Index := 0 to Pred(Screen.FormCount) do
  if (Screen.Forms[l_Index].ClassName <> 'TGUITestRunner') then
  begin
   l_ActiveForm := Screen.Forms[l_Index];
   break;
  end;//Screen.Forms[l_Index].ClassName <> 'TGUITestRunner'
 Assert(l_ActiveForm <> nil);
 l_Component := l_ActiveForm.FindComponent(l_Name);
 Assert(l_Component <> nil);
 aContext.PushObject(l_Component);
end;

- as an “active” form ANY form that DIFFERS from DUnit-form (TGUITestRunner) is regarded. Of course, this is just an “example”.

In the REAL LIFE it is “easily fixed”.

FOR NOW Draft of the project is here - https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/GUITests/Chapter3/

It is interesting? Should I write on this theme this weekend?

Or should I write about the “face” for FM-projects for DUnit?

... to be continued ...


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

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