infometis blog

Eleganter zum einfachen Report über Jenkins-Testdurchführung - Infometis

Geschrieben von Bernhard Fuchs | 02.03.2020 09:33:36

Im Blog Einfacher Testreport aus Tosca über Jenkins-Testdurchführung habe ich in Form einer ersten Version beschrieben, wie sich mit einfachen Mitteln ein grundlegender Testreport aus den Resultaten der Testdurchführung mit Jenkins erstellen lässt. Die inzwischen gesammelte Erfahrung zeigt, dass der dort beschriebene Ansatz sich in den meisten Fällen als nicht genügend effizient erweisen dürfte. Die Verwendung von Tosca-Testfällen zur Report-Erstellung erfordert verhältnismässig viel Zeit und die Vorgehensweise skaliert nicht. Denn der Aufwand zur Implementierung, Wartung und Durchführung steigt mindestens linear mit der Anzahl an Testfällen, die im Report zu berücksichtigen sind. Zunächst angedacht wurde eine andere Art des Auslesens der relevanten Daten aus den XML-Dateien sowie die Verwendung von XML-Modulen in Tosca. Im weiteren Verlauf hat sich gezeigt, dass der Umweg über Tosca nicht erforderlich ist. Im Folgenden lege ich dar, wie sich derselbe Testreport deutlich effizienter mittels eines PowerShell-Skripts erzeugen lässt.

1. Allgemeines

Aus Gründen des Aufwands und des Zwecks erfolgt die Reporterstellung schneller, wenn dazu keine Tosca-Testfälle erforderlich sind. Stattdessen erfolgt der Prozess zur Erstellung eines Testreports wie in Abb. 1 dargestellt.


Abb. 1: Transformationsprozess der XML-Resultatdatei in einen HTML-Testreport

Die XSL-Transformation der Jenkins-Resultatdateien vom XML- ins CSV-Format erfolgt sequentiell mittels eines PowerShell-Skripts, das am Ende der Testdurchführung ausgeführt wird. Aus diesen Rohdaten wird schliesslich ein Testreport im HTML-Format generiert.

2. Erstellung des Testreports im CSV-Format

Eine beispielhafte XML-Datei als Resultat der Jenkins-Testdurchführung sieht wie folgt aus (vgl. Listing 1):

<?xml version="1.0" encoding="utf-8"?>
<testsuites>
  <testsuite name="INT | Finanzieren - Smoketests" tests="10" failures="1" skipped="1" time="720.5927653" timestamp
    ="2020-02-10T16:12:56" id="39f13ba7-6820-d6dc-8165-76fbcceabc7e" log="">
    <testcase name="Finanzieren Smoketests - Grundstück erfassen" time="168.5116337" timestamp="2020-02-10T16:13:0
      5.6008759+01:00" log="+ Passed Finanzieren Smoketests - Grundstück erfassen&#xD;&#xA;…" />
    <testcase name="Finanzieren Smoketests - Versicherung erfassen" time="93.8767338" timestamp="2020-02-10T16:16:5
      8.9749463+01:00" log="+ Passed Finanzieren Smoketests - Versicherung erfassen&#xD;&#xA;…" />
    <testcase name="Finanzieren Smoketests - Finanzierung zusammenstellen" time="143.0023053" timestamp="2020-02-10
      T16:19:37.3683063+01:00" log="+ Failed Finanzieren Smoketests - Finanzierung zusammenstellen&#xD;&#xA;…" />
    <testcase name="Finanzieren Smoketests - Kreditportfolio überprüfen" time="" timestamp="">
      <skipped />
    </testcase>
    …
  </testsuite>
</testsuites>

Listing 1: Beispielhafte Jenkins-Resultatdatei im XML-Format

Die relevanten Daten werden jeweils aus dem Beginn des Elements <testcase> extrahiert, und zwar aus diesem Teil:

testcase name=“Finanzieren Smoketests – 01_Grundstück erfassen“ time=“168.5116337″ timestamp=“2020-02-10T16:13:05.6008759+01:00″ log=“+ Passed …

 

Der XML-Transformation ins CSV-Format liegt das folgende XSL-Stylesheet zugrunde (vgl. Listing 2):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="/">
    <xsl:for-each select="testsuites/testsuite/testcase">
      <xsl:value-of select="@name"/><xsl:text>;</xsl:text>
      <xsl:choose>
        <xsl:when test="@timestamp = ''">
          <xsl:text>Nicht ausgeführt;</xsl:text>
        </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="substring(@timestamp,0,11)"/><xsl:text>&#x20;</xsl:text><xsl:value-of
          select="substring(@timestamp,12,8)"/><xsl:text>;</xsl:text>
      </xsl:otherwise>
      </xsl:choose>
      <xsl:choose>
        <xsl:when test="not(@log)">
          <xsl:text>--&#xD;&#xA;</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="substring(@log,3,6)"/><xsl:text>&#xD;&#xA;</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Listing 2: XSL-Stylesheet für die XML-Transformation

Mittels des folgenden PowerShell-Skripts werden aus dieser Jenkins-Resultatdatei im XML-Format die für den Testreport relevanten Daten extrahiert und im CSV-Format gespeichert (vgl. Listing 3). Zugrunde liegt dabei ein ähnliches Skript, das unter XSL Transformation von XML-Dokumenten per PowerShell beschrieben wird. Aufgelistet sind nur die inhaltlich relevanten Funktionen (z.B. ohne Pfadprüfung oder Fehlerbehandlung).

# XML-Transformation mit einer XSLT-Datei ins CSV- und HTML-Format
# Skript-Aufruf: *.ps1 [XML-QuellDatei(en)] [XSLT-Datei] [Fachgebiet]

# Parameterinitialisierung
param
 (
  $xml = @("D:\Tosca\Resultat\Jenkins\Finanzieren\Eigenheim_Resultat.xml","D:\Tosca\Resultat\Jenkins\Finanzieren\
    Smoketests_Resultat.xml"),
  $xslt = "D:\Tosca\Resultat\XSL\XSL-Stylesheet.xsl",
  $area = "Finanzieren"
 )
 
# Funktion für die XML-Transformation
function XML-Transformation($xml, $xsltFile, $area)
{
  # Zeitstempel erstellen
  $timestamp = ([datetime]::now).tostring("yyyy-MM-dd HH-mm")
  
  # Temporäre CSV-Ausgabedatei definieren
  $csv = "D:\Tosca\Resultat\XSL\Testreport.csv"
  
  # Resultatdateien mit Pfad initialisieren
  $resultCSV = "D:\Tosca\Resultat\XSL\Report_" + $area + "_$timestamp.csv"
  $resultHTML = "D:\Tosca\Resultat\XSL\Report_" + $area + "_$timestamp.html"
  
  # Header in die CSV-Datei schreiben
  Add-Content -Path $resultCSV -Value 'Testfall;Zeitstempel;Testergebnis'

  # Array der XML-Inputdateien bearbeiten
  ForEach ($xmlFile in $xml) {

    $xmlFile = resolve-path $xmlFile

    # XSL-Objekt erstellen
    $script:xslt = new-object system.xml.xsl.xslcompiledtransform
    $script:xslt = new-object system.xml.xsl.xsltransform

    $xslt.Load($xsltFile) # Xslt-Datei laden

    $xslt.Transform($xmlFile, $csv) # XSLT-Transformation ausführen

    # Ergebnis in Resultatdatei anhängen
    Add-Content -Path $resultCSV -Value (Get-Content $csv) -Encoding UTF8
  }

  # Temporäre Ausgabedatei löschen
  Remove-Item $csv
}

# Funktionsaufruf zur XML-Transformation
XML-Transformation $xml $xslt $area

Listing 3: PowerShell-Skript zur XSL-Transformation ins CSV-Format

Die folgenden Parameter können beim Skriptaufruf übergeben werden:

-xml Jenkins-Resultatdateien im XML-Format als kommagetrennte Liste
-xsl Pfad zur XSLT-Stylesheet-Datei (optional, wenn im Skript hinterlegt)
-area Fachbereich (z.B. Finanzieren, Zahlungsverkehr)

Ein weiterer Parameter könnte dazu verwendet werden, die Ablage der generierten Reportdateien zu bestimmen.

Das Ergebnis im CSV-Format sieht beispielsweise so aus:

Finanzieren Smoketests – Grundstück erfassen;2020-02-10 16:13:05;Passed
Finanzieren Smoketests – Versicherung erfassen;2020-02-10 16:16:58;Passed
Finanzieren Smoketests – Finanzierung zusammenstellen;2020-02-10 16:19:37;Failed
Kreditportfolio überprüfen;Nicht ausgeführt;–

3. Transformation ins HTML-Format

Da der Import ins binäre Format von Microsoft Excel kein einfacher Prozess ist und der Testreport primär über das reine Testergebnis informieren soll, wird dafür das HTML-Format eingesetzt. Wie im vorherigen Blog erwähnt, liesse sich der Excel-Import mittels Tosca durchführen. Darauf wird hier jedoch verzichtet.
Die Transformation der CSV-Rohdaten in einen HTML-Testreport erfolgt mit dem zweiten Teil des PowerShell-Skripts am Ende der Funktion zur XML-Transformation (vgl. Listing 4; reduziert auf die wesentlichen Inhalte):

  $head = @"
    <title>Testreport</title>
    <meta charset="utf-8">
    <style type="text/css">
      BODY{background-color:#FFFFFF;color:black;font-family:Arial,sans-serif;font-size:14px;margin:25pt;}
      TABLE{border-width:2px;border-style:solid;border-color:black;border-collapse:collapse;}
      TH{border-width:1px;padding:8px;border-style:solid;border-color:black;background-color:#D5D8DC;
         font-size:18px;text-align:left;}
      TD{border-width:1px;padding:8px;border-style:solid;border-color:black;background-color:#F9F9F9;}
    </style>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js"></script>
    <script type="text/javascript">
      jQuery(document).ready(function(){
      jQuery('td:contains("Passed")').css('background-color','#2ECC71');
      jQuery('td:contains("Failed")').css('background-color','#EC7063');
      jQuery('td:contains("--")').css('background-color','#D0D3D4');
      });
    </script>
"@

    $body = "<h1>Testreport $area</h1>"

    Import-Csv -Delimiter ";" -Path $resultCSV -Encoding UTF8 | ConvertTo-Html -Head $head -Body $body | Out-File
      $resultHTML

Listing 4: PowerShell-Skript zur Erstellung des HTML-Testreports aus der CSV-Datei

Der Testreport im HTML-Format lässt sich beispielsweise so wie in Abb. 2 gestalten:

Abb. 2: Testreport im HTML-Format

4. Jenkins-Konfiguration

Die Konfiguration für die Jenkins-Testdurchführung ist in Listing 5 dargestellt:

pipeline {
    agent any
    options {
       timeout(time: 120, unit: 'MINUTES')
    }
    parameters {
        choice(name: 'ENV', choices: ['CT', 'INT'], description: 'Selektiere Environment')
    }
    environment {
        TRICENTIS_CI_HOME = 'C:\\Program Files (x86)\\TRICENTIS\\Tosca Testsuite\\ToscaCommander\\ToscaCI\\Client'
        TRICENTIS_CI_CONFIG = 'D:\\Tosca\\Konfigurationen\\'
        TRICENTIS_CI_ENVIRONMENT = "${params.ENV}"
        JUNIT_RESULT_FILE = 'D:\\Tosca\\Resultat\\Jenkins\\Finanzieren'
    }
    stages {
        stage('Kreditantrag erstellen') {
            steps {
                parallel("Eigenheim": {
                    echo "Starten der Tosca-Tests für Finanzierung: Eigenheim"
                    bat label: 'Eigenheim', script: '"%TRICENTIS_CI_HOME%\\ToscaCIClient.exe" -t junit -r "%JUNIT_
                      RESULT_FILE%\\Eigenheim_Resultat.xml" -m distributed -c "%TRICENTIS_CI_CONFIG%\\%TRICENTIS_CI
                      _ENVIRONMENT%_Finanzieren_Eigenheim.xml"'
                }
                )
            }
        }
        stage('Prozessneutrale Tests') {
            steps {
                parallel("Smoketests": {
                    echo "Starten der Tosca-Tests für Finanzierung: Smoketests"
                    bat label: 'Smoketests', script: '"%TRICENTIS_CI_HOME%\\ToscaCIClient.exe" -t junit -r "%JUNIT_
                      RESULT_FILE%\\Smoketests_Resultat.xml" -m distributed -c "%TRICENTIS_CI_CONFIG%\\%TRICENTIS_
                      CI_ENVIRONMENT%_Finanzieren_Smoketests.xml"'
                }
                )
            }
        }
        stage('Testreport') {
            steps {
                parallel("Testreport": {
                    echo "Starten des Testreports"
                    bat label: 'Testreport', script: '"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.
                      exe" -ExecutionPolicy Bypass -Command "D:\\Tosca\\Resultat\\XSL\\XML-Transformation.ps1" -xml
                      "%JUNIT_RESULT_FILE%\\Eigenheim_Resultat.xml,%JUNIT_RESULT_FILE%\\Smoketests_Resultat.xml"
                      -area "Finanzieren"'
                }
                )
            }
        }
    }
}

Listing 5: Beispielhafte Jenkins-Konfiguration

5. Versand per E-Mail

Mit PowerShell lässt sich diese Reportdatei im HTML-Format als Anhang automatisch an die gewünschten Empfänger versenden (vgl. Emails mit PowerShell versenden). Der Testreport kann auch direkt in die E-Mail integriert werden, wenn das HTML-Format zugelassen ist. Mit der Version zur HTML-Generierung in Listing 4 geht dabei die farbliche Hervorhebung des Testergebnisses verloren, da die Ausführung von JavaScript bei E-Mails im HTML-Format aus Sicherheitsgründen meist deaktiviert ist. Dafür können bei der Umwandlung des CSV-Reports in HTML-Code in der Spalte «Testergebnis» CSS-Klassen für die visuelle Unterscheidung eingesetzt werden. Die Skript-Definitionen mit den jQuery-Funktionen sind dann nicht erforderlich. Das entsprechend angepasste Skript findet sich in Listing 6:

  $head = @"
    <title>Testreport</title>
    <meta charset="utf-8">
    <style type="text/css">
      BODY{background-color:#FFFFFF;color:black;font-family:Arial,sans-serif;font-size:14px;margin:25pt;}
      TABLE{border-width:2px;border-style:solid;border-color:black;border-collapse:collapse;}
      TH{border-width:1px;padding:8px;border-style:solid;border-color:black;background-color:#D5D8DC;
         font-size:18px;text-align:left;}
      TD{border-width:1px;padding:8px;border-style:solid;border-color:black;background-color:#F9F9F9;}
      TD.passed {background-color:#58D68D}
      TD.failed {background-color:#EC7063}
      TD.unexecuted {background-color:#D0D3D4}
    </style>
"@

    $body = "<h1>Testreport $area</h1>"

    Import-Csv -Delimiter ";" -Path $resultCSV -Encoding UTF8 | ConvertTo-Html -Head $head -Body $body | ForEach
      { if ($_ -like "*<td>Passed*") {$_ -replace "<td>Passed", "<td class=`"passed`">Passed"} elseif ($_ -like
      "*<td>Failed*") {$_ -replace "<td>Failed","<td class=`"failed`">Failed"} else {$_ -replace "<td>--",
      "<td class=`"unexecuted`">--"}} | Out-File $resultHTML

Listing 6: Angepasstes PowerShell-Skript zur Erstellung des HTML-Testreports aus der CSV-Datei

Eine andere Option könnte der Versand einer E-Mail über ein Jenkins-Plugin sein, nachdem der Testlauf (resp. Build) ausgeführt worden ist. Diese Variante wurde jedoch nicht weiter geprüft, da sich der Prozess mit der gewählten Vorgangsweise direkter steuern lässt.

Das Passwort für den Zugang zum Mailserver sollte verschlüsselt abgelegt werden. Dies kann in PowerShell mit dem Code in Listing 7 erfolgen:

$KeyFile = "C:\Temp\AES256.key"
$PasswordFile = "C:\Temp\Passwort.txt"
$Password = "Passwort"

# Schlüsseldatei mit AES-256 erzeugen
$CryptoKey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($CryptoKey)
$CryptoKey | out-file $KeyFile

# Passwort verschlüsseln
$CryptoKey = Get-Content $KeyFile
$SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
$SecurePassword | ConvertFrom-SecureString -key $CryptoKey | Out-File $PasswordFile

Listing 7: Erzeugung eines verschlüsselten Passwortes in PowerShell

Der Testreport kann im Hauptteil der E-Mail mitgesandt werden (vgl. Listing 8 – reduziert auf die wesentlichen Inhalte, ohne Fehlerbehandlung).

# Zugangsdaten
$KeyFile = "C:\Temp\AES256.key"
$PasswordFile = "C:\Temp\Passwort.txt"
$MailUser = "Benutzername"

# SMTP-Einstellungen
$PSEmailServer = "mail.example.com"
$smtpFrom = "absender@example.com"
$smtpTo = "empfaenger@example.com"
$messageSubject = "Testreport " + $area
$messageBody = (Get-Content ".\Report_Finanzieren_2020-02-10 16-32.html" | Out-String)

# Zugangsdaten erzeugen
$CryptoKey = Get-Content $KeyFile
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $MailUser, (Get-Content
  $PasswordFile | ConvertTo-SecureString -Key $CryptoKey)

# E-Mail versenden
Send-MailMessage -Credential $creds -From $smtpFrom -To $smtpTo -Subject $messageSubject -Body $messageBody
  -BodyAsHtml -Encoding ([System.Text.Encoding]::UTF8) -port 587 -UseSsl

Listing 8: Automatischer E-Mail-Versand mittels PowerShell

Abhängig von der Konfiguration der konkreten Umgebung ist für Send-MailMessage anstelle der letzten beiden Parameter auch nur -port 25 zu verwenden, z.B. auf dem Jenkins-Server.

5. Verwendung von Jenkins-Plugins

Zu prüfen wäre der Einsatz von Jenkins-Plugins für denselben Zweck der Reportgenerierung und -verteilung basierend auf den XML-Resultatdateien, z.B. HTML Publisher oder Test Results Aggregator. Abhängig vom genauen Verwendungszweck erlaubt die hier beschriebene Vorgehensweise jedoch eine gezieltere Verwendung, indem die Konfiguration leichter oder überhaupt erst anzupassen ist (z.B. beim Versand von E-Mail-Anhängen).

6. Fazit

Diese Darlegung hat gezeigt, wie sich ein Report über die Durchführung von Tests mit Jenkins in wenigen Sekunden erstellen lässt. Im Vergleich zum Einsatz von Tosca-Testfällen erfolgt die Reportgenerierung in einem Bruchteil des vorherigen Zeitbedarfs. Der Konfigurationsaufwand ist deutlich geringer, insofern nicht mehr pro sachlichem Testfall Strukturen zu definieren sind, sondern nur noch einmal pro Fachgebiet und entsprechend der Gliederung der Jenkins-Resultatdateien. Diese Art der Reporterzeugung erlaubt einen vielseitigeren Einsatz. Um neu hinzugekommene Testfälle zu berücksichtigen, sind bei Bedarf nur die Jenkins-Konfigurationsdateien anzupassen. Zudem ist die Verteilung per E-Mail ein effizienter Weg zur direkten Information der relevanten Kreise.

 

2. März 2020, Bernhard Fuchs