- Posted by robert on Juli 2, 2010
Gestern der Coding-Dojo am Wasser war mal was anderes, aber auch sehr nett :-)
Die Ergebnisse werden noch aufbereitet, das Bild ist von Marco via Twitter.
- Posted by oliver on Juni 23, 2010
Oder: Wie ich Autofac so nutze, dass ich eine hilfreichere Fehlermeldung erhalte.
Wir nutzen Autofac und Autofac.Integration.Web mit PropertyInjection in einer BasePage-Klasse für Dependency Injection. Gestern bin ich (mind. schon zum zweiten Mal) über folgenden Fehler gestolpert (siehe auch Titel):
Der Fehler rührte – wie ich nach einigem Debuggen herausfand – von einer NullReferenceException in einem Constructor einer bei im Autofac-Container registrierten Klasse her. Leider verschwieg mir die Exception aber, um welche Klasse es sich hierbei handelte. Problematisch, wenn man mehrere Zig oder gar Hundert Klassen registriert hat.
Meine erste Idee, dieses Problem in Zukunft zu umgehen und direkt an die tatsächliche ursächliche Exception zu gelangen, war jeglichen Code aus dem Konstruktor in eine Init()-Methode zu verfrachten, die dann im Stack-Trace als Fehlerquelle auftreten sollte. Leider klappte das nicht – der Stack-Trace sah genauso aus wie vorher.#
Glücklicherweise zeigt der Stack-Trace in unseren NUnit-Tests sowohl den Constructor als auch die Init()-Methode als Fehlerquelle an. Nur hatte mein Test das Fehlerszenario leider nicht berücksichtigt. Jetzt tut er es.
Vielleicht hilft die Info ja jemandem weiter.
Gruß, Oliver
- Posted by robert on Juni 22, 2010
Krasse Sache und schön zu sehen, dass sich in diese Richtung etwas tut. Ein Gewinn für die Windows und die Linux Welt.
[via Miguel de Icaza]
- Posted by robert on Juni 13, 2010
Gute Software Architektur ist schwer und das Feld noch sehr jung. Obwohl sich DI und IoC auch im Mainstream durchzusetzen scheinen und die PoEAA Stück für Stück in den Wortschatz von Entwicklern einkehren, ist noch Platz nach oben.
Einen inkrementellen Fortschritt könnten EBCs darstellen. In diese Richtung zu denken, kann schnell und praktisch unseren Entwickleralltag bereichern. Wer lernen und sich über die Konzepte von EBC austauschen möchte, tut dies am besten auf der neuen Mailingliste: Event based Components.
- Posted by robert on Juni 11, 2010
Die Qualtität unserer Abläufe wird besser und besser :-) Folgendes Bild, zeigt wie Test-Läufe nach jedem Deployment von www.camping.Info dokumentiert werden. (In anderen Projekten haben wir ähnliche Abläufe. )
Wir haben noch viele ambitionierte Ziele für die Verbesserung der Deployment-Prozesse, zum Beispiel das Einbinden von Selenium-Tests oder das vollautomatische Deployment mit automatischen Rollback im Fehlerfall, so dass bei einer Auslieferung überhaupt keine manuellen Schritte mehr notwendig sind und ArbeitLebenszeit nur für einfache Kontrolle aufgewendet wird.
Was ein Entwickler (Oliver) in Vollzeit und ein Tester/Admin (Mark), der mit wenigen Stunden unterstützt hier leisten und geleistet haben finde ich beachtlich! Hut ab an Euch Beide! Sehr positiv finde ich auch die Synergien für die Entwicklungen von E-Commerce-Connector, aber auch für Beratungsleistungen. Teamwork-Helps!
- Posted by Stefan on Juni 10, 2010
The Grinder ist ein gutes Werkzeug, um automatisch Lasttests gegen eine beliebige Anwendung zu fahren. Sehr beliebt ist dabei das Testen von Web-Anwendungen, wozu nämlich das bei Grinder mitgelieferte HTTP-Plugin bestens geeignet ist.
Grundlegendes
Um den Grinder zum laufen zu bringen, sind ein paar Dinge zu beachten, bevor es richtig losgehen kann.
Java
Grinder ist eine Java-Anwendung. Also erst einmal Java installieren, falls noch nicht vorhanden. Eine aktuelle Version ist erforderlich.
Jython-Skripte
Grinder benutzt Jython, eine implementierung der Skriptsprache Python in Java, als Skript-Sprache. Das heißt, die Lasttests, die ausgeführt werden sollen, werden in Python geschrieben (keine Angst, ist nicht schwer). Eine installation von Jython ist nicht unbedingt notwendig, da Grinder bereits den Kern von Jython, den er selbst zum Laufen braucht, mitbringt. Wenn man jedoch weitere Python-Module (z.B. re für Regular Expressions) benutzen will, braucht man eine komplette Jython-Installation und muss Grinder dazu bringen, diese zu benutzen.
Organisation
Grinder ist dazu ausgelegt, realistische Szenarien zu simulieren. So kann zum Beispiel das zu testende System von verschieden Seiten gleichzeitig mit Anfragen belastet werden. Fangen wir am besten so an: Es gibt die Grinder-Konsole, von der aus mehrere Agents bedient werden können. Jeder Agent ist normalerweise auf einem eigenen Rechner. Jeder Agent startet eine konfigurierbare anzahl an Workern. Jeder worker schreibt ein eigenes Logfile. Jeder Worker startet wiederum eine gewisse anzahl Threads. Jeder Thread lädt nun das Python-Skript in dem der Test beschrieben ist und initialisiert es. Erst dann kommt der eigentliche Spaß: Jeder Thread fährt eine, wiederum konfigurierbare, anzahl an Runs. Was in einem Run passiert, ist dann Sache des Skriptes.
Das ging jetzt vielleicht ein bisschen schnell. Ein Beispiel, dass alle Ebenen ausnutzt wäre: 2 Agents mit je 4 Workern, zu je 10 Threads mit jeweils 100 Runs. Bei diesem Szenario sind am Ende die Daten von 2*4*10*100 = 8000 Testläufen auf 8 Logfiles und 2 Rechner verteilt. Zum Glück gibt es die Grinder-Konsole, die in solch einem Szenario prima geeignet ist, da sie die Daten sammelt und einfache statistische Auswertungen macht.
In meinem kommenden Beispiel ist es jedoch viel einfacher: 1 Agent mit 1 Worker zu 2 Threads mit jeweils 5 Runs. Dies ist dahingehend gut, da ich einerseits den einen Agent später auch, ohne die Konsole zu benutzen, automatisch per Skript bedienen kann. Andererseits entsteht nur 1 Logfile, welches ich mit einem eigenen Tool auswerten kann, um detailliertere Statistiken zu erstellen, als die Grinder-Konsole es erlaubt.
Hilfs-Skripte
Obwohl die Grinder-Konsole mehrere Agents bedienen kann, muss man diese jedoch vorher selbst starten. Erst dann verbinden sie sich mit der Konsole und nehmen Befehle entgegen. Da zum Starten des Agents jedoch einige Umgebungsvariablen gesetzt werden müssen, bietet es sich an, folgende Skripte in einem Ordner abzulegen (Die Pfade natuerlich anpassen, je nach dem wo Grinder abgelegt ist):
setGrinderEnv.cmd
1: set GRINDERPATH=C:\Projects\Camping.Info\_LoadTest\grinder
2: set GRINDERPROPERTIES=%GRINDERPATH%\start\grinder.properties
3: set CLASSPATH=%GRINDERPATH%\lib\grinder.jar
Falls man seine eigene Jython-Installation benutzen will:
1: set GRINDERPATH=C:\Projects\Camping.Info\_LoadTest\grinder
2: set JYTHONPATH=C:\Projects\Camping.Info\_LoadTest\jython2.5.1
3: set GRINDERPROPERTIES=%GRINDERPATH%\start\grinder.properties
4: set CLASSPATH=%JYTHONPATH%\jython.jar;%GRINDERPATH%\lib\grinder.jar
startAgent.cmd
1: call setGrinderEnv.cmd
2: java -cp %CLASSPATH% net.grinder.Grinder %GRINDERPROPERTIES%
startAgentWithPropertiesArg.cmd
1: call setGrinderEnv.cmd
2: java -cp %CLASSPATH% net.grinder.Grinder %1
startConsole.cmd
1: call setGrinderEnv.cmd
2: java -Duser.language="en" -cp %CLASSPATH% net.grinder.Console
Nun sollte es genug der Vorbereitung sein und es kann los gehen.
Starten von Grinder
Als allererstes starten wir die Konsole (startConsole.cmd sollte es tun):

Noch sind die Buttons zum Bedienen der Agents (oben links) deaktiviert. Das liegt daran, dass wir noch keinen Agent gestartet haben. Bevor wir das tun können, muss der Agent jedoch konfiguriert werden (wir erinnern uns an die verschiedenen Szenarien, die ich weiter oben vorgestellt habe). Dies funktioniert mit der Datei grinder.properties. Die datei kann auch anders heißen, wenn der Dateiname beim starten des Agents als Argument angegeben wird.
grinder.properties
1: #Das hier angegebene Skript wird gestartet
2: grinder.script = my_script.py
3:
4: grinder.hostID = local
5:
6: #Anzahl der Worker
7: grinder.processes = 1
8:
9: #Anzahl Threads pro Worker
10: grinder.threads = 2
11:
12: #Anzahl Runs pro Thread
13: grinder.runs = 5
14:
15: #Mit Konsole verbinden (bei false startet der Agent das Skript sofort, Daten werden immer gelogt.)
16: grinder.useConsole = true
17:
18: #Adresse anpassen, falls der Agent auf einer anderen Maschine startet als die Konsole
19: grinder.consoleAddress = localhost
20:
21: grinder.logDirectory = log
22: grinder.numberOfOldLogs = 0
23:
24: #Pfad zum Cachen von Python-Modulen. Bei Bedarf anpassen!
25: grinder.jvm.arguments = -Dpython.cachedir=C:\\Projects\\Camping.Info\\_LoadTest\\grinder\\start\\tmp
Nun sollte man mit startAgent.cmd einen Agent starten können:
1: 6/10/10 8:20:28 AM (agent): The Grinder 3.4
2: 6/10/10 8:20:28 AM (agent): connected to console at localhost/127.0.0.1:6372
3: 6/10/10 8:20:28 AM (agent): waiting for console signal
Jetzt könnte man den Agent schon loslaufen lassen, indem man auf den Button ganz links in der Toolbar der Konsole klickt. Aber halt! Wir haben das Teststkript my_script.py ja noch gar nicht geschrieben:
my_script.py
1: import sys
2: from net.grinder.script.Grinder import grinder
3: from net.grinder.script import Test
4: from net.grinder.plugin.http import HTTPRequest
5:
6: request1 = HTTPRequest(url = "http://www.google.com")
7: request2 = HTTPRequest(url = "http://www.bing.com")
8:
9: def test1():
10: request1.GET('/')
11: request1.GET('search?q=speak-friend')
12:
13: def test2():
14: request2.GET('/')
15: request2.GET('search?q=speak-friend')
16:
17: google = Test(1, "Google").wrap(test1)
18: bing = Test(2, "Bing").wrap(test2)
19:
20: class TestRunner:
21: def __call__(self):
22: for i in range(3):
23: google()
24: bing()
Ganz am anfang werden Module eingebunden, so wie man das bei C# mit using machen würde.
Danach definieren wir 2 Request-Objekte. Diese HTTPRequest-Klasse ist ein spezielles Grinder-Plugin, welches automatisch Zeiten misst und statistische Daten aufzeichnet.
Daraufhin werden 2 Methoden definiert: test1() und test2(). Diese rufen jeweils für die entsprechende Suchmaschine erst die Startseite und dann die Suche nach “speak-friend” auf.
Nun kommt der komplizierteste Teil, nämlich das, was bei Grinder “Instrumentation” heißt. Erst bauen wir uns einen neuen Test, der die Nummer 1 und den Namen “Google” bekommt: Test(1, "Google") – danach wird dieser Test um unsere vorher definierte methode test1 gewrappt. Wenn man nun google() aufruft wird im Prinzip test1() aufgerufen, jedoch werden dabei aufgezeichnete Statistiken (in diesem Fall von unserem HTTPRequest) unter der Testnummer 1 mit der Bezeichnung “Google” aufgezeichnet, ins Logfile geschrieben und ggf. an die Konsole gesendet! Jeder Aufruf auf google() ergibt dabei einen eintrag in der Test-Statistik. Die aufgezeichneten Werte werden dabei addiert. Eine Zeile im Logfile bzw. ein gelisteter Test in der Konsole bezieht sich also auf die Summe aus einem Startseitenaufruf und einer Suche.
Eine Sache, die noch sehr wichtig ist: das HTTPRequest-Plugin unterstuetzt per default Cookies. Diese werden automatisch zurückgeschickt. Jedoch werden sie am anfang jedes Runs gelöscht. Daher bietet es sich an, in einem Run den Test mehrmals auszuführen. Dies wird auch getan in der Klasse TestRunner. Was in der methode __call__ (soweit ich Python verstehe ist wird diese methode aufgerufen, wenn man die Klasse selbst wie eine methode benutzt, also “TestRunner()”) steht definiert einen Run. Hier werden in jedem Run die Tests “Google” und “Bing” jeweils 3 mal ausgeführt. Pro Run landen also 6 Einträge im Logfile. Nochmal zur Erinnerung: Jeder dieser Einträge setzt sich aus der Summe von 2 Requests (Startseite und Suche) zusammen.
Zusammenfassend sollten bei dieser Konfiguration 1 Agent * 1 Worker * 2 Threads * 5 Runs * 6 Tests * 2 Requests = 120 Requests gesendet werden, 60 auf Google und 60 auf Bing. Wem das zu viel ist, der kann ja die Konfiguration anpassen. An sonsten: Los! (Button in der Grinder-Konsole klicken, OK klicken, denn wir wollen die Konfiguration aus grinder.properties nehmen)
Es ist sinnvoll, dabei den Agent im Auge zu behalten. Nach Ablauf der Tests sieht es (bei einer lausigen Internetverbindung) etwa so aus:
Ich komme nicht ganz auf 60 Tests, da bei einer Exception (hier wahrscheinlich wegen Timeout) immer der ganze Run abgebrochen wird. Die Spalte “Response Bytes per Second” ist übrigens Unsinn, da dort einfach die Summe von ResponseLength durch die Gesamtlaufzeit dividiert wird.
Auswertung
Es lohnt sich, nun einen Blick in die entstandenen Logfiles zu werfen. Das File, dessen Nama mit “data” beginnt, enthält alle gesammelten Testergebnisse. Es ist im CSV-Format und kann daher einfach in ein Tabellenkalkulationsprogramm oder ein eigenes Auswertungs-Tool geladen werden. Es ist auch möglich selbst definierte Werte in das Logfile zu schreiben. Außerdem lässt sich der Lasttest auch automatisch über einen Continuous-Integration-Server wie TeamCity fahren. In TeamCity kann man dann sogar einen selbst definierten Lasttest-Report einbinden. Wie das alles funktioniert, werde ich in späteren Blogposts beschreiben.
Bis dahin: Happy Coding
Stefan Noack von Speak-Friend
- Posted by robert on Juni 10, 2010
Ist zu viel Konstruktor-Injizierung ein Anti-Pattern? In einem Blog-Post konstruiert Jeff Palermo ein Beispiel, in dem eine injizierte Komponente eine wirklich langsame Initialisierung hat. Da die Komponente nur von einer Methode der Klasse benötigt wird, leitet er ab, dass statt der Konstruktor-Injizierung von Komponenten eine Lazy-Factory die bessere Alternative ist.
Die Kommentare lehnen diesen Schluss fast fast einhellig ab, denn:
1) Eine Verzögerung der Initialiszierung ist auch über den Container und mit .NET 4 auch über System.Lazy<> zu erreichen.
2) Gegen eine Schnittstelle zu programmieren bedeutet gerade, dass man Implementierungsdetails nicht in der Hand hat.
3) Langwierige Initialisierung in der Komponente bedeutet, dass die Komponente minderer Qualität ist, hier muss dann nachgebessert werden. Es sollten keine aufwändigen Operationen in der Konstruktor-Implementierung durchgeführt werden.
4) Ein Abstrakte Fabrik erzeugt ein Abhängigkeit gegenüber dieser, was die Menge der Abhängigkeiten steigert und die Vorteile des IoC reduziert. Jedes „new“ ist bad „news“.
5) Um häufige Initialiserung zu umgehen, könnten Services als Singelton konfiguriert werden, wobei globale Konfiguration ein Problem sein kann, weil sie schwer zu Debuggen und auf Anwendungsebene auch schwer nachzuvollziehen sein kann.
Danke an Jeff Palermo für seinen sehr lesenwerten Post!
Links:
- Posted by robert on Juni 6, 2010
BDD bietet die Chance, Entwicklungsprozesse zu verbessern. Wo wir sind, wohin wir wollen und wie wir dies erreichen können, darum geht es in diesem Blog-Post.
Bestandsaufname
Manchmal schreiben wir BDD inspirierten Code, doch wir erreichen nicht das wesentliche Ziel von BDD: "Q&A und nicht-technische Projektteilnehmer sollen besser gemeinsam arbeiten können".
Vereinzelt wird eine Spezifikation im BDD-Style einem Kunden vorgelegt, doch unsere User-Stories sind eher Aufgabensammlungen die komplett losgelöst von der Implementierung stehen. Überspitzt gesagt, ist BDD-Style bei uns eher TDD, mit einer anderen Namenskonvention.
Wo soll es hin
Ich würde vorschlagen, dass wir die zentrale Idee von BDD auch für uns übernehmen, also: "Wir möchten BDD Nutzen um die Kommunikation zwischen Auftraggeber, UI-Team, Entwicklern und Q&A zu verbesseren." Was bedeutet das für die einzelnen Rollen:
Product Owner/ Auftraggeber
- Arbeitet mit User-Stories und Szenarien in einem vorgegebenem Format
- Bekommt Vogelperspektiven Sicht auf Implementierungsfortschritt
Tester/ Q&A
- Automatische Tests werden aussagekräftiger, der Quelltext wird zugänglicher, weil leichter verständlich
- Kann Testszenarien in Entwickler-freundlicher Sprache formulieren und den Umsetzungfortschritt (passiv) nachvollziehen
- Kann Testszenarien direkt im Quelltext definieren
- Ist wie ein "Product-Owner" für die Q&A Sicht
UI-Team
- Hat einen besseren Blick auf die Entwicklungsgeschehnisse
- Kann leichter beim Spezifizieren unterstützen und gleich das richtige Format für Projektmanager, Kunden und Entwickler liefern
Entwickler
- Implementierungsschritte und Fortschritt werden von Nicht-Technikern nachvollzogen
- Muß in einer Form arbeiten, die die tatsächliche Implementierung der Spezifikationen an das nicht technische Team-dokumentiert
Konkrete Schritte
1: Anforderungserfassung: Der erste Schritt den wir gehen müssen ist es Anforderungen, User-Stores etc. in einem BDD tauglichen Format zu erfassen. (Siehe dazu auch weiter unten das Template)
2: Implementierungsfeedback: Damit alle sehen können wo wir sind, benötigen wir einen Feedback-Mechanismus der Teil der Buildprozess ist, also am Besten vom Continous-Integrations-Server erzeugt wird.
(Screenshot zeigt StoryQ HTML-Report)
Template für BDD-Spezifikation
Das Template für die BDD-Spezifikation ist Denglish, hier sollten wir überlegen ob wir Deutsch verwenden, wobei wir Methoden und Klassen (dort wo die Konzepte auch in englisch existieren) eigentlich in Englisch umsetzen
Titel (Beschreibung der User-Story, auch Mehrzeilig)
- Die User-Story erzählt:
- As a [Rolle]
- I want [Features]
- So that [Nutzen]
Akzeptanz Kriterium: (als Szenarien)
- Szenario 1: Title
- Given [Kontext]
- And [Mehr Kontext]...
- When [Ereignis]
- Then [Ergebniss]
- And [Anderes Ergebniss]...
- Szenario 2: ....
- Szenario 3: ...
Framework oder Konvention
Frameworks gibt es wie Sand am Meer. Welches ist das Richtige? Wie sieht unser Prozess aus, wie wird aus einer User-Story Code? Wie können wir dafür sorgen, dass die Umsetzung und der Umsetzungstand nicht nur beim Implementierer bleibt, sondern auch für alle zur Verfügung steht. Folgende Kriterien könnten wir verwenden:
- Alle Teammitglieder, insbesondere nicht-technische sollen einfachen Zugriff auf den Implementierungsstand erhalten. Das Framework sollte gute Reports erzeugen oder die Reports sollten gut zu erweitern sein.
- Ideal wäre eine Integrationsmöglichkeit mit Target-Process (hier können wir aber vielleicht die entsprechenden User-Stories-Ids angeben und so zumindest Links erzeugen). Eine Integration über die Testfälle wäre denkbar?
Spezifikation != Test-Driven-Development
Mir scheint es wichtig, dass Spezifikationen nicht mit TDD verwechselt werden und das eine BDD Prozess der viele Rollen umschließt, ergänzend zu Unit- und Integrationstests steht. Unsere Kunden oder das UI-Team interessieren Implementierungsdetails, die vielleicht Test-Getrieben mit Unit-Tests entwickelt wurden nicht. BDD in meinem Verständniss sollte Geschäftslogik-relevant sein. Um BDD-Tests von anderen Tests abgrenzen zu können, könnten sollten Sie vielleicht in einer eigenen Ordnerstruktur landen. Eine Datei und Klassen Namenskonvention sollte BDD-Tests deutlich abheben.
Das Feedback (z.B.: HTML Reports) über den Implementierungsstatus und die Spezifikation an Nicht-Entwickler sollte also nur BDD-Tests umfassen bzw. diese zumindest alleine stellen.
Links:
- Posted by robert on Juni 3, 2010
Gen2 und Gen0 sind deutlich gegenläufig, versteh ich nicht, hat jemand ein Erklärungsmuster? Fängt der Garbarge-Collector an in Gen2 aufzuräumen weil viel neuer Speicher allokiert werden muß?
Bild: Speicher für Camping.Info Server: (8 GIG RAM, 2x Quadcore, ca.: 1000 aktive Sessions, Requests per Second: Avg.: 50, Peak: 120)

Das 10x mehr Speicher in Gen2 liegt bedeutet natürlich, dass wir unser Memory-Leak immer noch nicht aufgespürt haben. Normal müsste Gen 2 kleiner sein als Gen 0? Viele Fragen :-)
(Robert)
- Posted by robert on Mai 31, 2010
Die Tools um NHiberante herum werden besser und mehr. Nachdem im letzten Jahr mit NHProf ein hervorragender Profiler erschien der uns schon manches mal sehr hilfreich war, kommt mit “Visual NHibernate” ein Code-Generator der sowohl von der Datenbankseite, als auch von der Modelseite her funktioniert.

Aus meiner Sicht ist für uns ist NHProf jedoch zunächst wenig interessant, da wir zukünftig für neue Projekte mit Fluent-NHibnerate auf Mapping per Konvention setzen wollen und bestehende Projekte ja Ihre Mapping schon haben.
Um bestehenden Legacy-Projekte den Wechsel auf ORM zu erleichtern, scheint Visual NHibernate bestens geeignet zu sein.
Links:
(Robert)