Direkt zum Hauptbereich

Printer tests best practice...

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

Beliebte Posts aus diesem Blog

Neuer JSON Serialisierer ab Embarcadero Tokio 10.2

Seit Embarcadero Tokyo 10.2 veröffentlicht hat, steht uns Entwicklern ein neuer sehr gut zu gebrauchender JSON Marshaller zur Verfügung. Leider ist die Dokumentation dürftig. Das schöne ist aber, das Embarcadero sich ziemlich an gängige Verfahren hält. So konnte ich z.B. sehr einfach herausfinden was TJsonCustomCreationConverter<T> macht, nachdem ich C# Beispiele analysierte. Da es leider keine offizielle Dokumentation zum TJsonSerializer gibt, beruhen meine Beispiele auf Versuch und Irrtum. Es gibt 3 Namespaces, welche relevant sind. Zum einen "System.JSON.Serializers", für den eigentlichen Serialisierer und die Klassenattribute, zum anderen "System.JSON.Converters", für eigene Konverter und zuletzt "System.JSON.Types", für z.B. Datumsformate oder Formatierungen. TJsonSerializer Ist die wichtigste Klasse, sie ist implementiert im Namespace System.JSON.Serializers. Diese Klasse ist dafür zuständig, ein Objekt in JSON (Serialisierung) zu formati...

Delphi mit Facebook per OAuth2 verbinden

Als ich mich das erste mal mit OAuth2 zur Autorisierung beschäftigte, begann ich mit der RESTDemo.exe von Delphi zu experimentieren. Dies hat aber nicht so einfach funktioniert wie ich dachte. Also beschreibe ich hier mal mein Vorgehen wie ich mich mit Facebook Autorisieren kann. Die Vorarbeit liegt nicht unbedingt auf Seite Delphi, sondern genauso auf der Seite Facebook. Registrierung Facebook Als erstes muss man seine APP auf  https://developers.facebook.com/apps  registrieren. Als nächstes, geht man auf Einstellungen - Allgemeines und holt sich die "App-ID", in Delphi "Client-ID" und "Client-Secret", bzw. "App-Geheimcode". Beides ist später für die Url nötig. Wichtig in dem Zusammenhang, als "App Domain" "localhost" einzutragen. Um den Status der App auf Live zu stellen ist von Seite Facebook ein Links zur Datenschutzrichtlinie erforderlich. Hatte hierfür die Url meines Arbeitgebers eingetragen. Um die App Live zu s...

Wer ist Nathan Thurnreiter

Das bin ich Nathan Thurnreiter. Ein mit Leib und Seele begeisterter Delphi Programmierer. Viele Entwickler von heute kennen sich sehr gut aus wie man schnell was erstellt und ändert. Aber können Prinzipien wie SOLID oder LOD einfach nicht richtig anwenden, bzw. haben es nicht verinnerlicht. Dies bewegte mich endlich, selbst einen Blog zu eröffnen und den mal in deutsch. Nun aber mal ein bisschen zu mir. Bin über Umwege zur Informatik gestossen. Hatte zuerst Automobilmechaniker gelernt und dann Jahre beim Studium von Maschienenbau/Fahrzeugtechnik und technischer Physik verbracht, biss ich dann zum Programmieren kam. Dies begann mit Pascal bzw. Delphi und ich bin fast ausschliesslich dabei geblieben. Natürlich habe ich wie jeder Programmierer mal über den Tellerrand geschaut. Aber bis auf ein paar nervige Kleinigkeiten ist Delphi für mich die Sprache schlecht hin. Momentan leite ich ein kleines Entwickler Team, welches mit C# und Delphi ein Warenwirtschaftssystem erstellt. Die nächste...