Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Massentests mit DUnitX (https://www.delphipraxis.net/207386-massentests-mit-dunitx.html)

manumeter44 19. Mär 2021 06:55

Massentests mit DUnitX
 
Schönen guten Tag zusammen,
ich bin gerade dabei mich in DUnitX einzuarbeiten und frage mich, ob es in diesem Test-Framework von Delphi möglich ist Massentests ohne ewig langen Code hinzubekommen.

Beispiel:
Ich habe eine Funktion vorliegen welche einfach nur 2 Werte, die als Eingangsparameter mitgegeben werden addieren soll:
function TCalc.Add(x, y: Real): Real;
begin
Result := x + y;
end;

Und diese Funktion soll nun nicht mit "nur" 2 [TestCase] auf ihre Richtigkeit hin überprüft werden sondern mehrere Male.Ich stelle mir das etwa so vor:
[Test]
[TestCase('Add Test','0,0,0')]
[TestCase('Add Test','0,1,1')]
[TestCase('Add Test','0,2,2')]
[TestCase('Add Test','0,3,3')]
[TestCase('Add Test','0,4,4')]
.
.
.
.
.
[TestCase('Add Test','10,10,20')]
procedure TestAdd(Value1, Value2, Expected: Real);

Die Frage, die sich mir nun stellt ist: Wie ist es möglich (wie oben drüber dargestellt) so viele Testfälle darzustellen ohne diese explizit einzeln zu definieren? Kann man TestCases irgendwie durchloopen oder eine andere Möglichkeit sich bei "Massentests" im Code kurz zu halten?

Mavarik 20. Mär 2021 05:10

AW: Massentests mit DUnitX
 
Wie wäre es mit...


Delphi-Quellcode:
begin
  for I:=0 to 1000 do
    begin
      if Add(A,B) <> C then
        begin
          Assert.Fail;
          exit;
        end;
   end;
   Assert.Pass;
end;
Und A und B kannst Du dann entsprechen vorgeben, errechnen.. was auch immer

Uwe Raabe 20. Mär 2021 08:55

AW: Massentests mit DUnitX
 
Der Nachteil bei dem Ansatz mit der for-Schleife ist, dass der Test beim ersten Fehler abbricht und Fehler in höheren Schleifendurchgängen erst entdeckt werden, wenn der Fehler behoben wurde. Aus diesem Grund wurden ja überhaupt dieses TestCase-Attribut erfunden.

Man kann das aber auch mit einem TestDataProvider lösen (hier zur Übersicht alles in einer Unit):
Delphi-Quellcode:
unit TestUnit1;

interface

uses
  DUnitX.Types, DUnitX.InternalDataProvider, DUnitX.TestDataProvider, DUnitX.TestFramework;

type
  TCalcSourceProvider = class(TTestDataProvider)
  private
    FMaxTest: Integer;
  public
    constructor Create; override;
    function GetCaseCount(const methodName: string): Integer; override;
    function GetCaseName(const methodName: string): string; override;
    function GetCaseParams(const methodName: string; const caseNumber: Integer): TValueArray; override;
  end;

type
  [TestFixture]
  TCalcTestObject = class
  public
    [Test]
    [TestCaseProvider(TCalcSourceProvider)] // alternativ auch: [TestCaseProvider('CalcSourceProvider')] (siehe initialization)
    procedure TestAdd(Value1, Value2, Expected: Double);
  end;

implementation

constructor TCalcSourceProvider.Create;
begin
  inherited;
  FMaxTest := 11*11;
end;

function TCalcSourceProvider.GetCaseCount(const methodName: string): Integer;
begin
  Result := FMaxTest;
end;

function TCalcSourceProvider.GetCaseName(const methodName: string): string;
begin
  Result := methodName;
end;

function TCalcSourceProvider.GetCaseParams(const methodName: string; const caseNumber: Integer): TValueArray;
var
  A: Double;
  B: Double;
begin
  A := caseNumber div 11;
  B := caseNumber mod 11;
  Result := TValueArray.Create(A, B, A + B);
end;

procedure TCalcTestObject.TestAdd(Value1, Value2, Expected: Double);
begin
  { Hier testen wir erstmal, ob unser Dataprovider überhaupt funktioniert. Wenn das klappt, ersetzen wir das mit dem Test der TCalc-Methode Add }
  Assert.AreEqual(Expected, Value1 + Value2, 0.1);
end;

initialization
  TestDataProviderManager.RegisterProvider('CalcSourceProvider', TCalcSourceProvider);
  TDUnitX.RegisterTestFixture(TCalcTestObject);
end.

Bernhard Geyer 20. Mär 2021 10:53

AW: Massentests mit DUnitX
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1485584)
Der Nachteil bei dem Ansatz mit der for-Schleife ist, dass der Test beim ersten Fehler abbricht und Fehler in höheren Schleifendurchgängen erst entdeckt werden, wenn der Fehler behoben wurde. Aus diesem Grund wurden ja überhaupt dieses TestCase-Attribut erfunden.

Wenn man solche Tests hat, sollte die SW-(Version/Hotfix/Release) erst freigegeben werden wenn keine der Tests fehl schlagen.
D.h. dieser Schleifen-Ansatz hätte für die Veröffentliche SW-Version keinen Nachteil.

Und diese Schleifenansatz hat den Vorteil das man eine großen Menge an Fällen testen kann. Das alles in tausenden Zeilen Attribute zu schreiben kostet nur zeit das zu schreiben.
Der Attribut-Weg ist ja Fälle gedacht, in denen ich spezielle Werte testen will, aber nicht eine ganzen größeren Block.

Rollo62 20. Mär 2021 11:18

AW: Massentests mit DUnitX
 
Die Frage ist doch auch woher die Testdaten dann eigentlich kommen.
Eine vorgegebene Tabelle, empirische Daten, per Zufall, oder gibt es sogar eine Formel ?

Ich bin prinzipiell etwas gegen vorgegebene Tabellen, denn damit ist meist die Idee des Testers verbunden, und seltene Fälle werden nie erkannt.

Mavarik 20. Mär 2021 13:42

AW: Massentests mit DUnitX
 
Uwe's Ansatz mit dem Provider finde ich richtig gut (+1) - kannte ich noch nicht - aber "meine" Schleife reicht mir in der Regel..
Wenn ein Unittest fehl schlägt, dann fixe ich diesen... Wenn mir Testinsight anzeigt das 1000 fehlgeschlagen sind, hilft mir das nicht weiter,
sondern frustriert eher. I.d.R. fixed man doch einen Fehler nach dem anderen.

Wobei ich keinen Sinn darin sehe 1+1 1+2 1+3 1+4 und so weiter zu testen...
Das ist ein Computer, wenn er 1+1 rechnen kann, kann er auch 1+n rechnen. Es geht doch dann eher um die Corner-Cases... 1+0 1+-1 1+65568 1+0.5 oder was auch immer...

Mavarik :coder:

Stevie 20. Mär 2021 13:53

AW: Massentests mit DUnitX
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von Mavarik (Beitrag 1485613)
Wenn mir Testinsight anzeigt das 1000 fehlgeschlagen sind, hilft mir das nicht weiter

Wenn halt 1000 Tests fehlschlagen, dann sind 1000 Tests auch wieder grün, wenn du den Fehler fixt.

Der Witz ist aber, dass in den schlimmen Fällen der Code nur für eine bestimmte Kombination fehlschlägt.
Das ist weitaus hilfreicher als wenn der Test mit der Schleife einfach nur fehlschlägt.
Wenn du nämlich dann noch eine genaue Fehlermeldung ausgeben willst, mit welchen Werten das fehlgeschlagen ist, musste wieder unnötigen Code schreiben.

Leider benamt DUnitX die Tests, die über den TestDataProvider erzeugt werden ein bisschen lame - mit meiner Implementierung in Spring.Testing siehst du genau, mit welchen Werten der Test aufgerufen wurde.

Ein Beispiel, wie man sehr einfach Mengen von werten in einen Unittest stopfen kann, hab ich hier mal skizziert, um eine Funktion für die Primfaktorzerlegung zu testen. Unten kann man sehen, wie das dann im TestInsight (und auch wenn man das mit der oldschool DUnit GUI ausführen würde) ausschaut.

Ich kann dann genau den Test anhaken, der da fehlschlägt, und debuggen, ohne erst durch irgendne Schleife zu müssen und nen conditional Breakpoint zu setzen, der erst stoppt wenn der Wert kommt, der den Test fehlschlagen lässt, alles mühsamer Mist.


Im übrigen ist der Ansatz, die Testdaten von extern zu bekommen gar nicht mal so abwegig, das nennt sich dann zum Beispiel "data driven testing" - da kann man tolle Sachen mit machen - natürlich in ganz anderem Ausmaße und nicht um nen Taschenrechner zu testen - obwohl, eigentlich braucht man fürn Taschenrechner bloß eine Testfunktion, die dann als Input die Formel hereinbekommt, die man eingetippt hat und das erwartete Ergebnis - und das kann man hervorragend in einer Textdatei oder so pflegen.

Uwe Raabe 20. Mär 2021 14:38

AW: Massentests mit DUnitX
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1485594)
Und diese Schleifenansatz hat den Vorteil das man eine großen Menge an Fällen testen kann. Das alles in tausenden Zeilen Attribute zu schreiben kostet nur zeit das zu schreiben.

Eben dafür gibt es ja auch den TestDataProvider.

Zitat:

Zitat von Stevie (Beitrag 1485615)
Im übrigen ist der Ansatz, die Testdaten von extern zu bekommen gar nicht mal so abwegig, das nennt sich dann zum Beispiel "data driven testing" - da kann man tolle Sachen mit machen

In der Tat! Ich benutze das z.B. für die Parser-Tests bei MMX. Die Testdaten sind ja in der Regel Code-Schnipsel oder ganze Units. Mit einem TestDataProvider, der einfach die Dateien in einem Verzeichnis auflistet, brauche ich nur eine neue Datei in das entsprechende Verzeichnis zu legen und schon laufen alle Tests auch mit dem neuen Code-Schnipsel - ohne dass ich das Testprojekt überhaupt neu compilieren muss. Das ist ganz praktisch, wenn man als Folder das Source-Verzeichnis von Delphi angibt. Da kommen schon mal ein paar Dateien zusammen.

Mavarik 20. Mär 2021 14:47

AW: Massentests mit DUnitX
 
Zitat:

Zitat von Stevie (Beitrag 1485615)

Wenn halt 1000 Tests fehlschlagen, dann sind 1000 Tests auch wieder grün, wenn du den Fehler fixt.

Der Witz ist aber, dass in den schlimmen Fällen der Code nur für eine bestimmte Kombination fehlschlägt.
Das ist weitaus hilfreicher als wenn der Test mit der Schleife einfach nur fehlschlägt.
Wenn du nämlich dann noch eine genaue Fehlermeldung ausgeben willst, mit welchen Werten das fehlgeschlagen ist, musste wieder unnötigen Code schreiben.

Wie gesagt ich kannte das nicht, daraus ergeben sich aber unzählige neue Möglichkeiten einige meiner Tests zu optimieren...

Bernhard Geyer 20. Mär 2021 15:14

AW: Massentests mit DUnitX
 
Zitat:

Zitat von Mavarik (Beitrag 1485613)
Wobei ich keinen Sinn darin sehe 1+1 1+2 1+3 1+4 und so weiter zu testen...

Macht sinn z.B. einen (langsamen) Algorithmus/Implementierung A durch einen Algorithmus/Implementierung B zu ersetzen.
Da macht es dann sinn möglich einen großen Teil der möglichen Input/Output-Kombinationen zu prüfen.

Bernhard Geyer 20. Mär 2021 15:18

AW: Massentests mit DUnitX
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1485618)
Zitat:

Zitat von Bernhard Geyer (Beitrag 1485594)
Und diese Schleifenansatz hat den Vorteil das man eine großen Menge an Fällen testen kann. Das alles in tausenden Zeilen Attribute zu schreiben kostet nur zeit das zu schreiben.

Eben dafür gibt es ja auch den TestDataProvider.

Kannte ich nicht. Muss ich mir mal anschauen :thumb:

Sind noch bei uns drüber auch die restlichen DUnit-Tests durch DUnitX-Tests zu ersetzen.
Mit Kopplung an Jenkins gibt da wenigstens eine gute Auswertung. Und mittlweile gibts die passende Unit auch direkt im git-Repository (DUnitX.Loggers.XML.JUnit.pas)

Rollo62 20. Mär 2021 15:28

AW: Massentests mit DUnitX
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1)
Mit einem TestDataProvider, der einfach die Dateien in einem Verzeichnis auflistet, brauche ich nur eine neue Datei in das entsprechende Verzeichnis zu legen und schon laufen alle Tests auch mit dem neuen Code-Schnipsel

Ja, das Filesystem ist eine gute Lösung für solche Dinge.
Man kann sich andere Arten von TestDaten auch z.B. über eine Datenbank hereinladen.
Die hätte den Vorteil "remote" konfigurierbar und erweiterbar zu sein.

So ein TestDataProvider macht viel Sinn, und nicht nur für Massentests.

Stevie 20. Mär 2021 16:47

AW: Massentests mit DUnitX
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1485627)
Sind noch bei uns drüber auch die restlichen DUnit-Tests durch DUnitX-Tests zu ersetzen.

Unnötig, DUnit kann komplett problemlos aufgebohrt werden, so dass es Attribute und anderen Schnick unterstützt, aber bestehende Tests einfach so weiter funktionieren.
Machen wir seit mind 2012.

Stevie 20. Mär 2021 19:27

AW: Massentests mit DUnitX
 
Bin gerade noch über ein anderes Argument für einzelne Tests Pro Datenkonstellation gestolpert.
Performancemessung (eine sehr grobe zugegeben) bekommst ja bei DUnit und co geschenkt - da kann man z.B. schnell sehen, obs für bestimmte Daten irgendwie langsamer läuft als für andere.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:12 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz