Vergleich von Lasttest-Tools

Es gibt sehr viele Programme, mit denen sich Lasttests durchführen lassen. Je nach Einsatzgebiet und Einsatzzweck sind unterschiedliche Programme geeignet.

In diesem Beitrag werden 5 gängige Tools mit einander verglichen und Vor- und Nachteile werden herausgestellt.

Test-Szenario

Es wird ein Programm zur Durchführung von automatisierten Lasttests gesucht, mit dessen Hilfe eine Webanwendung geprüft werden kann.

Das eingesetzte Tool soll über ein Reporting verfügen und Tests sollen relativ unkompliziert aufgesetzt werden können. Eine Reproduzierbarkeit der Tests ist sehr wichtig.

Die Web-Anwendung nutzt viel Javascript und Ajax-Calls, weshalb auch das eingesetzte Lasttest-Tool Ajax-Calls unterstützten muss.

Randbedingungen

  • Lasttest mit bis zu 20 Nutzern gleichzeitig muss simulierbar sein
  • Testen einer Website (http/https)
  • JavaScript/Ajax muss mit einbezogen werden bei Testausführung
  • unlogisches Nutzerverhalten simulieren (z.B. mehrmaliges vor und zurück, ändern der Sessionvariable in der URL usw.)
  • Reproduzierbare Tests ohne viel Aufwand beim Neustart der Tests
  • Reporting der Test-Läufe
  • Testskripte leicht zu erstellen und zu warten
  • Lauffähig auf Windows-Rechnern

Untersuchte Tools

Gatling

  • Letztes Release: 24.01.2019   (Stand 15.02.2019)
  • Dokumentation: https://gatling.io/docs/current/
  • Test-Anspruch: Lasttests
  • Vorteile:
    • Reporting in HTML-Format
    • Durchführung mehrerer Tests gleichzeitig
    • Testaufnahme im Browser möglich
  • Nachteile:
    • Es werden Scala-Dateien generiert, die über Batch-Programm ausgeführt werden. Für automatisiertes Testen von Vorteil. Bei Testern ohne Batch-Kenntnissen könnte die Ausführung zu einer Herausforderung werden.
    • Scripting- oder Programmiererfahrungen für Anpassungen der Tests notwendig.

Selenium

  • Letztes Release: Ende 2018   (Stand 15.02.2019)
  • Dokumentation: https://www.seleniumhq.org/docs/
  • Test-Anspruch: Regression-Tests von Web-Anwendungen
  • Vorteile:
    • Aufzeichnung und Anpassung über intuitives GUI.
    • Kann einfach als Chrome-Extension installiert und ausgeführt werden, um Tests auszuführen. Regression-Tests werden durch Selenium WebDriver als separate Anwendung zusammengestellt.
    • Tests können in Java geschrieben werden (Selenium WebDriver).
  • Nachteile:
    • Selenium ist nicht für Lasttests ausgelegt.

Tsung

  • Letztes Release: 30.08.2017   (Stand 15.02.2019)
  • Dokumentation: http://tsung.erlang-projects.org/user_manual/
  • Test-Anspruch: Laststest
  • Vorteile:
    • Hohe Performance und umfangreiche Test-Möglichkeiten.
    • Web-Interface für Test-Status und Reports.
  • Nachteile:
    • Es wird keine ausführbare Datei für Windows bereitgestellt.
    • Das Erstellen von Tests läuft über XML-Dateien. Es kann viel definiert werden und Tsung deckt viele Use Cases ab. Allerdings ist die Benutzung dadurch nicht einfach.
    • Das letzte Release liegt lange zurück. Unklar wie der Support in naher Zukunft aussehen wird.

WebLOAD

  • Letztes Release: Nov. 2018   (Stand 15.02.2019)
  • Dokumentation: https://www.radview.com/wp-content/uploads/2018/12/WebLOADQuickStartPRO.pdf
  • Test-Anspruch: Lasttests
  • Vorteile:
    • Umfangreiche Dokumentation (eBooks und Videos).
    • Tests können mit Hilfe eines Rekorders aufgezeichnet werden.
    • Umfangreiche Auswertungen.
    • Validierungen lassen sich über den Skript-Editor hinzufügen.
    • Integration mit Selenium möglich.
  • Nachteile:
    • Sehr viele Tools kommen zum Einsatz: WebLOAD Recorder, WebLOAD Console und WebLOAD Analytics. Das Aufsetzen und Ausführen der Tests ist daher mit einer gewissen Einarbeitungszeit verbunden.

JMeter

  • Letztes Release: 27.10.2018   (Stand 15.02.2019)
  • Dokumentation: http://jmeter.apache.org/usermanual/get-started.html
  • Test-Anspruch: Lasttest und Performance-Test
  • Vorteile:
    • Test IDE zum Aufnehmen der Tests über einen Browser
    • Erstellen von Testplänen
    • Auswertungen als HTML-Report
  • Nachteile:
    • Keine Ausführung von JavaScript

Zusammenfassung

JMeter kann kein JavaScript ausführen und deckt somit nicht alle Randbedingungen ab. Tsung ist längere Zeit nicht mehr aktualisiert worden, hier ist es unklar, wie der Support in Zukunft aussehen wird. Außerdem ist Tsung nicht auf Windows ausgelegt und eher für das Testen unter Linux entwickelt. Zudem ist das Aufsetzen der Skripts sehr technisch. Beide Tools eignen sich nicht für das untersuchte Szenario.

Selenium stellt eine sehr intuitiven Chrome-Extension zum Aufnehmen von Tests bereit. Damit lassen sich sehr schnell gute Tests erstellen, in dem z.B. HTML-Elemente anhand deren ID gesucht und die Werte abgeglichen werden. Selenium ist allerdings nicht für Lasttests ausgelegt, weshalb es für das beschriebene Test-Szenario nicht empfohlen wird.

Sowohl Gatling als auch WebLOAD decken das beschriebene Test-Szenario mit den genannten Rahmenbedingungen ab. Für WebLOAD spricht, dass eine Integration mit Selenium möglich ist. Somit lässt sich Selenium für Regression Tests nutzten und WebLOAD für Lasttests.

Was ist Stage / Staging?

Wofür steht STAGE in DEV-stage.it?

Die Staging-Umgebung ist eine Kopie der Produktionsumgebung.
In der Entwicklungsphase wird zunächst lokal in der Development-Umgebung entwickelt und präsentierfähige Stände auf die Stage-Umgebung hochgeladen. Typischerweise kommen Stage-Umgebungen bei Web-Projekten zum Einsatz.

Die Stage-Umgebung ist die Bühne für den Kunden.
Um den aktuellen Stand zu begutachten und frühzeitig Feedback geben zu können. Dabei ist es von Vorteil, wenn die Stage-Umgebung möglichst viele Aspekte der Produktionsumgebung (oder Live-Umgebung) vorweist. Das beinhaltet vor allem Daten, aber natürlich auch die Einstellungen der Seite, Farben, Seite, Funktionen usw.

Staging im 3-Phasen-Entwicklungsprozess

Die Entwicklung durchläuft bei uns die Phasen Analyse, Entwicklung und Support. Die Analyse findet dabei auch während der Entwicklung statt, wobei der Support das spätere Hosting der Seite und Übernahme von weiteren Supportätigkeiten beinhaltet.

das 3-Phasen-Entwicklungsmodell

Der Workflow in der Entwicklungsphase sieht die drei bereits genannten Umgebungen vor: Development, Staging und Production.

Die Staging-Umgebung kommt dabei in der Phase der Entwicklung zum Einsatz. Hier wird nun in regelmäßigen Abständen der aktuelle Stand „auf Stage hochgeladen“, damit sich der Kunde ein Bild von der umgesetzten Arbeit machen kann.

Der Stand auf Stage ist dabei nicht zwangsläufig abnahmefähig, sondern spiegelt viel mehr den aktuellen Entwicklungsstand wieder. Anstelle von Datenbankverbindungen kann zum Beispiel mit Mockups bzw. gefakten Datenbankzugriffen gearbeitet werden. Dies soll es ermöglichen, dass die Funktionen bereits ausprobiert werden können, bevor das Datenbanklayout im Programmcode umgesetzt worden ist.

Ist die Entwicklungsphase im 3-Phasen-Entwicklungsprozess abgeschlossen, kann Stage heruntergefahren werden. Alternativ bieten wir allerdings auch an, dass nach Projektende auch die Stage-Umgebung aufrecht gehalten wird. So haben Sie jederzeit eine nah an der Live-Version angelehnte Version, auf der Sie neue Funktionen oder Plugins testen können.


Wenn Sie Interesse an unseren Hosting- und Support-Leistungen haben, können Sie sich die aktuellen Tarife auf folgender Seite ansehen: https://dev-stage.it/hosting-inkl-domain/

REST-Webservice mit PHP ansprechen

In diesem Beitrag geht es um das Ansprechen eines REST-Webservices mittels PHP, wobei der REST-Webservice die Ergebnisse mit Hilfe von JSON zurückliefert. REST steht für Representational State Transfer und stellt eine Möglichkeit zur Implementierung einer Schnittstelle dar. Rückgaben können auf verschiedene Arten erfolgen, z.B. durch XML oder JSON. Das Ansprechen eines SOAP-Webservices ist mit Hilfe der in PHP eingebauten Soap-Klasse möglich (SoapClient). Für das Arbeiten mit einem REST-Webservice kann beispielsweise auf cURL zurückgegriffen werden, was auch Inhalt dieses Beitrags sein wird.

Methode zum Aufruf des Services

public function callAPI($data = false)
{
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_POST, 1);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
	curl_setopt($curl, CURLOPT_URL, $this->serviceUrl);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
	curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
	$ret = curl_exec($curl);
	if ($ret === false) {
		$ret = null;
	}
	curl_close($curl);
	return $ret;
}
Zunächst wird mit curl_init(); eine neue cURL-Instanz erzeugt. Post-Daten können eingesetzt werden (curl_setopt($curl, CURLOPT_POSTFIELDS, $data);) und die URL zum Webservice wird gesetzt (curl_setopt($curl, CURLOPT_URL, $this->serviceUrl);). Anschließend wird der Service aufgerufen und der Rückgabewert zurückgegeben ($ret = curl_exec($curl);). Wichtig ist, dass vor der Rückgabe die cURL-Verbindung geschlossen wird (curl_close($curl);).

Methode zum Auslesen der JSON-Rückgabe

Angenommen, in der Rückgabe stecken Benutzerinformationen als JSON-Objekt, kann das Auslesen mit folgende Methode vorgenommen werden:
public function getUserEntityFromInfo($userInfo) {
	$obj = json_decode($userInfo);
	if ($obj && isset($obj->{'username'})) {
		$user = new UserEntity();
		$user->setuserName($obj->{'username'});
		$user->setemail($obj->{'useremail'});
		return $user;
	}
	return null;
}
Dabei wird zunächst das JSON-Objekt anhand des Strings erzeugt ($obj = json_decode($userInfo);). Beinhaltet das Objekt username, so werden username und useremail ausgelesen. Die Klasse UserEntity besteht in diesem Beispiel lediglich aus Getter- und Setter-Methoden.

Die komplette Klasse

class RestExample {
	
	private $serviceUrl = "https://www.myservice.com/servicename";
	
	public function callAPI($data = false)
	{
		$curl = curl_init();
		curl_setopt($curl, CURLOPT_POST, 1);
		curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
		curl_setopt($curl, CURLOPT_URL, $this->serviceUrl);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
		$ret = curl_exec($curl);
		if ($ret === false) {
			$ret = null;
		}
		curl_close($curl);
		return $ret;
	}
	
	public function getUserEntityFromInfo($userInfo) {
		$obj = json_decode($userInfo);
		if ($obj && isset($obj->{'username'})) {
			$user = new UserEntity();
			$user->setuserName($obj->{'username'});
			$user->setemail($obj->{'useremail'});
			return $user;
		}
		return null;
	}
}

Aufruf der Klasse

Das folgende Beispiel zeigt, wie diese Klasse aufgerufen und Post-Werte übergeben werden können.
$restClient = new RestExample();
$curlPostData = array(
		'MyFirstProperty' => 123,
		'MySecondEntry' => "testdata"
);
$ret = $restClient->callAPI($curlPostData);
if ($ret != null) {
	$user = $restClient->getUserEntityFromInfo($ret);
	if ($user != null) {
		echo $user->getuserName();
	}
	else {
		echo "Benutzerinformationen konnten nicht geladen werden.";
	}
}
else {
	echo "Keine Rückgabe nach Webservice-Aufruf erhalten.";
}

Chrome-Extension zum Testen eines REST-Webservices

Zum Schnellen Testen eines REST-Webservices, bietet sich die Google Chrome-Extension Advance REST Client an. Mit dieser Extension können sowohl GET- als auch POST-Requests getestet werden und das Übergeben von Variablen ist möglich.

Anmerkungen

Die hier gezeigten Beispiele sind bewusst recht simpel gehalten. Das Aufrufen eines REST-Webservices wird gezeigt und es ist ersichtlich, wie POST-Daten übergeben werden können. Auch das Erzeugen eines JSON-Objektes wird gezeigt.

Auslesen einer HTML-Tabelle mit JavaScript und Auswertung von Input-Feldern

Das folgende Beispiel zeigt, wie mit Hilfe von JavaScript-Bordmitteln eine HTML-Tabelle durchlaufen werden kann. Anhand des Beispiels soll darüber hinaus eine Möglichkeit gezeigt werden, Input-Felder in den Tabellen-Zeilen auszulesen und auszuwerten.

Aufbau der HTML-Tabelle

<table id="table_example">
      <tbody>
        <tr>
          <th>Ausgewählt</th>
          <th>Bezeichnung</th>
          <th>Wert 1</th>
          <th>Wert 2</th>
        </tr>
        <tr>
          <td><input name="activated" type="checkbox" checked=""></td>
          <td>Tabellen-Inhalt erste Zeile</td>
          <td><input name="input1" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="2"></td>
          <td><input name="input2" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="1"></td>
        </tr>
        <tr>
          <td><input name="activated" type="checkbox" checked=""></td>
          <td>Tabellen-Inhalt zweite Zeile</td>
          <td><input name="input1" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="2"></td>
          <td><input name="input2" class="form-control" onchange="parse_int_value(this);" style="text-align:right;" type="text" value="1"></td>
        </tr>
      </tbody>
    </table>

In der Tabelle gibt es in jeder Zeile eine Checkbox und zwei Eingabefelder. Eine JavaScript-Funktion soll nun die einzelnen Zeilen durchlaufen und auswerten, ob die Zeile markiert wurde und welche Werte eingetragen sind. Bei den Eingabefeldern sollen nur Integer-Werte erlaubt sein, weshalb der eingegebene Inhalt zunächst überprüft wird.

JavaScript zum Parsen der Input-Inhalte

function parse_int_value(ctrl) {   
ctrl.value = parseInt(ctrl.value) || 0;
}

Das Parsen der Eingabewerte erfolgt beim Durchlaufen der Tabelle erneut.

JavaScript zum Durchlaufen der Tabelle

    function parse() {
     var tblOutputDiv = document.getElementById('tbl_values');
     tblOutputDiv.innerHTML = "";
     var table = document.getElementById('table_example');
     var tmp = "";
	 	 for (var i = 1, row; row = table.rows[i]; i++) {
			//iterate through rows and check whether the row has been selected
			var checked = false;
			var value1 = 0;
			var value2 = 0;
			var inputFields = new Array();
      //get all input fields in the current row
			inputFields = row.getElementsByTagName("INPUT");
			for (var counter = 0, field; field = inputFields[counter]; counter++) {
				if (field.name == "activated") {
					checked = field.checked;
				}
				else if (field.name == "input1") {
					value1 = parseInt(field.value) || 0;
				}
				else if (field.name == "input2") {
					value2 = parseInt(field.value) || 0;
				}
			}
      tmp += 'Ausgewählt: ' + (checked ? "ja" : "nein");
      tmp += ' Wert 1: ' + value1;
      tmp += ' Wert 2: ' + value2;
      tmp += '<br/>';
		}
    tblOutputDiv.innerHTML = tmp;
    }

Zunächst wird das DIV geladen welches zur Ausgabe dient und dessen Inhalt überschrieben. Anschließend erfolgt eine Iteration durch die Zeilen der Tabelle und in jeder Zeile werden die darin enthaltenen Input-Elemente geladen. Diese werden ebenfalls durchlaufen und anhand des Namens werden die Werte in einer temporären Variable zwischengespeichert und später in dem Ausgaben-DIV ausgegeben.

Aufgerufen wird das Skript durch einen Link Tabelle auslesen

Dieses Beispiel ist bewusst simpel gehalten und soll lediglich das Grundprinzip verdeutlichen.