Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Unit testen von Timer gesteuerem Code (https://www.delphipraxis.net/206991-unit-testen-von-timer-gesteuerem-code.html)

TurboMagic 15. Feb 2021 12:18

Unit testen von Timer gesteuerem Code
 
Hallo,

wie kann ich folgendes Szenario DUnit testen?

Ein Handler für Empfangene Daten meldet korrekt empfangene Daten über ein Event.
In gewissen Fehlerfällen startet er einen Timer, der wird ggf. zurückgesetzt falls
noch weitere Daten eintreffen und erst wenn dieser abgelaufen ist wird über ein
Event der fehlerhafte Datenempfang gemeldet (es ist aber wichtig das mitzubekommen,
daher die Meldung).

Wie kann ich den Unittest so bauen, dass er die für den Time nötige Zeit wartet und
erst danach prüft, ob das Event gekommen ist und mit welchen Parametern?

Grüße

TurboMagic

Der schöne Günther 15. Feb 2021 12:51

AW: Unit testen von Timer gesteuerem Code
 
Indem man den Timer bzw. die Zeitmessung von außen übergibt und ihn das nicht selbst erstellen lässt.

Bei allem was mit "Zeit" zu tun hat fühlte es sich (zumindest für mich) anfangs etwas komisch an, aber mein Leben wurde danach so viel einfacher. Die paar Zeilen sich einen
Delphi-Quellcode:
ITimer
oder
Delphi-Quellcode:
IDateTimeProvider
zu schreiben war es echt wert. Und die Unit-Tests mussten nicht mehr echte Millisekunden warten sondern waren auch viel schneller fertig 😉

PS: Wenn es nur um das "Tick"-Event an sich geht dann bietet sich es sich auch an, von außen einfach ein Event (z.B. IEvent<T> aus Spring4D) zu setzen und das Event halt passend in deinem Unit-Test auszulösen.

TurboMagic 15. Feb 2021 16:51

AW: Unit testen von Timer gesteuerem Code
 
Danke für den Tipp. Hab' mir jetzt was interface mäßiges gebastelt, habe aber aber
noch ein Problem gefunden, dass ich jetzt umschifft habe aber noch nicht verstehe warum
es aufgetreten ist:

Das Interface ist ja ein TInterfacedObject und damit referenzgezählt.
Es wird auch nur eine Interface Referenz erzeugt die von der Unit Test Klasse und von
der zu testenden Klasse benutzt wird (Constructor Injection).

Im TInterfacedObject wird eine Logging Klasse von mir über ein globales Singleton genutzt und
im Destructor dieser Klasse noch eine Logmeldung abgesetzt.

Wenn ich die Interface Referenz nicht im TearDown der UnitTest Klasse auf nil setze, dann kommt
das Finalization der Log-Klassen Unit mit Freigabe des Log-Objektes vor der Freigabe des
Interfaces! Darin wird dann natürlich auf das Log Objekt zugegriffen welches inzwischen nil ist,
da die Freigabe mittels FreeAndNil erfolgte...

Warum in dem Fall diese ungewöhnliche Reihenfolge der Aufrufe?
Mit dem expliziten auf Nil setzen funktioniert es ja, möchte aber den Grund verstehen.

Der schöne Günther 15. Feb 2021 19:04

AW: Unit testen von Timer gesteuerem Code
 
Du hast dir doch die Erklärung schon selbst gegeben:

Zitat:

Zitat von TurboMagic (Beitrag 1483021)
dann kommt
das Finalization der Log-Klassen Unit mit Freigabe des Log-Objektes vor der Freigabe des
Interfaces!

DUnit wird wohl so funktionieren dass bis zum bitteren Ende eine Instanz deiner Testklasse vorliegt, und da zeigt die Referenz halt noch auf dein
Delphi-Quellcode:
IGedöns
.

Ich würde
  1. Diese Log-Meldung im Destruktor optional/abschaltbar machen. Hier scheinst du sie ja auch nicht zu brauchen
  2. Das
    Delphi-Quellcode:
    meinGedöns := nil
    im
    Delphi-Quellcode:
    TearDown()
    drin lassen. Ich könnte mich irren, aber ich meine das brauchst du auch insbesondere, wenn du mit deinen Unit-Tests Speicherlecks finden willst, da vergleicht er ja im Endeffekt auch nur "Vor SetUp()" mit "nach TearDown()" und wenn am Schluss mehr da ist als vorher war, dann ist es ein Speicherleck.

Mavarik 18. Feb 2021 16:36

AW: Unit testen von Timer gesteuerem Code
 
Wie Günter schon geschrieben hat. Ein TimeService ist immer hilfreich.
Man kann auch einen normalen Event nehmen ohne Spring und nutzt den WaifFor(TimeOut) davon, so
kann man zwei Fälle abdecken. 1.) Event is gekommen oder 2. TimeOut.
Ich gebe dann gerne eine Procedure mit, das sieht dann so aus:
Delphi-Quellcode:
var
  WasCalled : boolean;
  E        : TEvent;
  R        : TWaitResult;
begin
  WasCalled := false;

  E := DoSomeTing(Procedure
    begin
      WasCalled := true;
    end);

  R := E.WaitFor(5000);

  if R = wrSignaled
    then Assert.IsTrue(WasCalled)
    else Assert.Fail('TimeOut'); // or error

  E.Free;
end;
Ich hoffe ich hab Dich richtig verstanden...

TurboMagic 18. Feb 2021 17:04

AW: Unit testen von Timer gesteuerem Code
 
Hallo,

ich hab' mir jetzt einen Timer mit Interface drumherum gebaut.
Der hat das Enable gewrappt und ein zusätzliches Flag für den Unit Test.
Damit lege ich fest, ob OnTimer des Timers über den Timer kommen soll
(regulärer Betrieb) oder wenn eine bestimmte "Operation ist zuende"
Methode aufgerufen wird (Unit Test Betrieb), die im normalen Betrieb
einfach nichts tut.

Funktioniert für mich.

Grüße und Danke
TurboMagic


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:54 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