Software ist heute einfacher zu entwickeln, nichts desto trotz steigt die Komplexität der heutzutage entwickelten Software. Dadurch steigt auch die Anzahl möglicher Fehler. Es ist jedoch nahezu unmöglich sämtliche Ausnahmefälle abzudecken. Deshalb ist kontinuierliches Testen der Software meiner Meinung nach, ein wichtiger und notwendiger Bestandteil jeder der Softwareentwicklung. Allzu oft wird heute noch, dies missachtet. Viel wird vermeintlich schnell behoben, was sich später bitterlich rächt. Einer dieser schnell schnell Punkt, ist das anpassen von Reports.
In der Realität erlebe ich oft, das zu Reports meist gar keine automatischen Test existieren. Meist mit der Begründung, das geht nicht oder es existieren ja sowieso keine automatischen Tests. Vielleicht kommt noch, das testet dann der Kunde.
Sucht man nach passenden antworten wie man Ergebnisse von Reports testen kann, kommt "Verwende Unittests". Das ist richtig, man sollte die gesamten vorlagerten Klassen mit Unittests abdecken, gar keine Frage. Dies nimmt einem schon viel Arbeit und oder Fehlersuche ab. Existieren keine, steigt das Risiko eines Ping Pong Spiel Exportpotential. Es kommt des Öfteren vor, das an Reports Anpassungen vorgenommen werden, welche spätere wieder verworfen werden. Passt man nach einer weiteren Rückmeldung die erste an, ist die zweite wieder weg. Um dies zu verhindern sollten man schrittweise Reports mit Tests abdecken. Mir ist klar, das dies nur die Spitze der Testpyramide abdeckt, aber genauso wichtig. Gerade wenn die Basis an Unitttests fehlt oder im Aufbau ist. Um ein sicheres Code Review vorzunehmen, muss die Qualität der Report gewährleistet sein.
Für was braucht man solche Tests?
Bespiel: Mit Unitttest kann man prüfen ob die entsprechenden Daten geliefert werden, aber kommen sie auch wirklich so am Drucker an wie gewünscht? Wird der richtige Schacht angesprochen? Ist der richtig Drucker gewählt worden.
Mit z.B. Delphi ist es nicht möglich einen Druckertreiber zu entwickeln, man benötigt dazu ein DDK von Microsoft. Alternativ kann aber ein Printserver per RAW Protokoll über Port 9100 verwendet werden. Mit diesem kann mindestens getestet werden ob alle Felder auch gedruckt werden.
In der Windows Welt werden Drucker auf Printservern mit 2 Arten von Ports eingerichtet. LPR-Ports (Port 515) oder Standard TCP/IP (Port 9100, 9101,9102) Ports.
Wie fange ich an?
Im Grunde habe ich meines Erachtens 2 Möglichkeiten. Die eine ist, schreibe einen virtuellen Print Server. Die andere bedient sich der Möglichkeit, den Druck in eine Datei auszulagern.
Print Server
Hatte dafür einfach einen TCP Server geschrieben, der auf Port 9100 hört. Der Print Server empfängt die Daten und bestimmt selbst was er damit anfängt. Ob man hier die Daten direkt validiert oder weiterreicht ist jedem selbst überlassen. Ich prüfte die ankommenden Daten direkt mit einem Referenzbeleg. Auch hier ist der Drucker ein "Generic / Text", damit die Daten einigermaßen vergleichbar waren. Prüfte ich aber z.B. ob der richtige Schacht gesetzt ist, kam ein PostScript Printer zum Einsatz. Hier mal ein Beispiel in Delphi, ist aber in jeder Sprache möglich.
unit Main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdContext, IdCustomTCPServer,
IdTCPServer, IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdSNMP,
Vcl.StdCtrls;
type
TForm3 = class(TForm)
IdSNMP1: TIdSNMP;
IdTCPServer1: TIdTCPServer;
FBtnActive: TButton;
procedure IdTCPServer1Execute(AContext: TIdContext);
procedure FBtnActiveClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.FBtnActiveClick(Sender: TObject);
begin
IdTCPServer1.Active := True;
end;
procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
IdTCPServer1.Active := False;
end;
procedure TForm3.IdTCPServer1Execute(AContext: TIdContext);
var
DataStream, FileStream : TStream;
begin
DataStream := TMemoryStream.Create;
AContext.Connection.IOHandler.ReadStream(DataStream, -1, true);
ShowMessage(Format('Es wurden %d Byte empfangen', [DataStream.Size]));
FileStream := TFileStream.Create('c:\Temp\teest.txt', fmCreate);
DataStream.Position := 0;
FileStream.CopyFrom(DataStream, DataStream.Size);
FileStream.Free;
DataStream.Free;
end;
end.
Einrichten eines neuen Netzwerkanschlusses unter Windows geht wie folgt.
![]() |
Neuen TCP/IP Anschluss auswählen |
![]() |
Weiter klicken |
![]() |
IP oder hier "localhost" eintragen |
![]() |
Benutzerdefinierte Einstellung wählen |
![]() |
Übernehmen oder eigenen Port wählen |
![]() |
Fertig |
Ausgabe in Datei umleiten
Um einen beliebigen Druck in eine Datei auszugeben, geht man unter Windows wie folgt vor.
Als erstes richten wir unter Windows einen Drucker ein. Dazu gehen wir zur Systemsteuerung - Hardware und Sound - Geräte und Drucker und fügen einen neuen Drucker hinzu.
![]() |
Printer hinzufügen |
![]() |
Neuen Anschluss hinzufügen oder weiter zu... |
![]() |
Abb. 2.1 Vorhandenen Anschluss |
![]() |
Wohin der Druck ausgegeben werden soll |
![]() |
Für Textausgabe den Printer "Generic / Text Only" |
![]() |
Anschluss des Drucker prüfen bzw. setzen |
Man kann hier auch einen anderen Druckertreiber verwenden. Der "Generic / Text Only" vereinfacht eine erste grob Analyse des Ausdruck. Es ist viel einfacher 2 Texte zu vergleichen als PostScript oder ähnliches.
Druckereinstellungen testen
Eine mir bekannte Möglichkeit Druckereinstellungen wie Schacht (Paper Tray) usw. zu Testen, wäre über unsere zuvor installierten Drucker. Meine derzeit bevorzugte Methode ist, einen neuen Drucker z.B. "Brother HL-6050 Dummy" einzurichten und den Port auf Textausgabe setzen. Jetzt können wir ganz normal einen Druck auslösen. Bei diesem Modell steht der Schacht an 5er Escape-Sequenzen in selbiger Print-Job Datei.
Auszug aus der Job.pnt Datei. Beispiel mit Kassette 1:%-12345X@PJL @PJL SET REPRINT=JOB @PJL SET HOLD=OFF @PJL JOB NAME="Testseite" @PJL PRINTLOG ITEM = 1,PRINTER @PJL PRINTLOG ITEM = 2,Sun,28 Sep 2014 15:20:32 @PJL PRINTLOG ITEM = 3,User @PJL PRINTLOG ITEM = 4,DEV2-DELPHI @PJL SET ECONOMODE=OFF @PJL SET MEDIATYPE=REGULAR @PJL SET RESOLUTION=600 @PJL ENTER LANGUAGE=PCL &u600D*t600R&l0O&l1h1001H&l26a6d1E&l0S&l1X&l0U&l0Z*p0Y*b1030M*b130w.....Auszug aus der Job.pnt Datei. Beispiel mit manuellem Papiereinzug:
%-12345X@PJL @PJL SET REPRINT=JOB @PJL SET HOLD=OFF @PJL JOB NAME="Testseite" @PJL PRINTLOG ITEM = 1,PRINTER @PJL PRINTLOG ITEM = 2,Sun,28 Sep 2014 15:21:0 @PJL PRINTLOG ITEM = 3,User @PJL PRINTLOG ITEM = 4,DEV2-DELPHI @PJL SET ECONOMODE=OFF @PJL SET MEDIATYPE=REGULAR @PJL SET RESOLUTION=600 @PJL ENTER LANGUAGE=PCL &u600D*t600R&l0O&l2H&l26a6d1E&l0S&l1X&l0U&l0Z*p0Y*b1030M*b130......
Nachzulesen in der PPD (Postscript Printer Description) von Brother.
*DefaultInputSlot: AutoSelect
*InputSlot AutoSelect/Printer Default: "&l7H"
*InputSlot Upper/Upper Paper Cassette: "&l1h1001H"
*InputSlot ManualFeed: "&l2H"
Closure
Anstelle eines Generic Text Druckers könnten man auch einen PostScript Drucker einsetzen um die Druckposition noch genauer abzubilden. Der VirtualPrintServer ist in der Lage jeden RAW Printstream weiterzugeben. Derzeit gibt er einfach nur Text weiter.
Kommentare
Kommentar veröffentlichen