dinsdag 29 november 2011

LAC 2011, dag 1


Vorige week ben ik bij het LAC (Landelijk Architectuur Congres) geweest in Nieuwegein. Ik ben nu een paar jaar Software Architect bij RBK en had dit ooit ergens ingevuld. Heel “toevallig” (toeval bestaat niet :-) ) kreeg ik een uitnodiging voor het LAC 2011. Aangezien ik nog genoeg te leren heb op dit gebied leek het me niet verkeerd om dit 2-daagse congres bij te wonen.

Het was een leuk, interresant en nuttig congres, waarbij ik veel geleerd heb. Het was wel even wennen: het was namelijk geen technisch congres (ik heb maar een keer Java en Oracle voorbij horen komen) wat ook te zien was aan de vele iPads en MacBooks (echte programmeurs hebben niet zo’n ding ;-) ). Veel algemene informatie gehoord, echter helaas weinig inhoudelijk over goede/slechte architecturen, tips, etc.

Over het algemeen staat niet vast wat een “architect” nu precies is, de meningen zijn nogal verdeeld. Per bedrijf en situatie is dit anders. Sowieso kunnen verschillende niveaus en typen aangegeven worden: Enterprise, Application, Infrastructuur/Netwerk, Informatie Architect etc.
Wel is duidelijk dat de architect van vroeger niet meer kan: gekscherend de “stugge dictator”. De technische ontwikkelingen gaan snel, veel veranderingen, en als bedrijf moet je snel kunnen veranderen. Dit betekent een flexibele architectuur die meehelpt in plaats van tegenwerkt.

De eerste keynote was van de CIO van ministerie van VWS. Zijn onderwerp: “doe maar klein en gewoon”. Architecten kunnen hele mooie diagrammen en techische documenten maken, en die kunnen echt “heel waar” zijn, maar niet-technische mensen begrijpen die totaal niet. Dus daar moet je bij het management niet mee aankomen. Beter is om een simpele tekening te maken en Jip & Janneke taal te gebruiken. Ook belangrijk is te weten wat bij bestuurders leeft: het aantal gigabytes of de betrouwbaarheid van de opslag, de snelheid of de veiligheid van geheime documenten in cloud?

De volgende keynote was van de Application Architect van de UBS Bank (Engeland). Hij beveelde aan om de “principles” goed te communiceren: plat gezegd het “waarom”. Als bijvoorbeeld op management niveau bepaalde beslissingen genomen worden, moet dit vertaald worden naar een globale technische structuur op het Enterprise niveau. Vervolgens wordt dit omgezet in gedetailleerdere structuur op Application niveau. Als laatste moeten de Developers dit uitvoeren.
Als alleen de kale eisen van hogerhand opgelegd worden, zullen de onderliggende lagen dit proberen te omzeilen. Daarom moet je het “waarom” steeds communiceren, zodat men het doel weet, dat het niet maar zo verzonnen is, etc. De mensen gaan dan meedenken en keuzes maken omdat men begrijpt waarvoor het is. Een belangrijk aspect is dat je feedback krijgt: bepaalde wensen kunnen tegen praktische problemen lopen. Normaal krijg je dit niet te horen, maar door de feedback krijgen de “hogere”lagen de bewustwording dat iets niet uitgevoerd kan worden.

Tussen de sessies door konden bedrijven in een Speakers Corner hun verhaal kwijt. Zo zal volgens Citrix het “Bring Your Own Device” concept steeds belangrijk worden. Werknemers hebben gemiddeld 3 devices (PC, Laptop, Smartphone, tablet, etc). Door deze via Citrix te verbinden kan de gebruiker overal zijn werk doen: sessies en data worden automatisch gesychroniseerd. De gebruiker kan vervolgens zelf zijn eigen device uitkiezen, meenemen of zelfs kopen...

De laatste sessies van de 1e dag die ik gevolgd hebben gingen over Agile en Architectuur. Deze zijn allebei nodig: de Waterfall methode is niet meer te doen in deze tijd, alleen in uitzonderlijke situaties zijn ze nog bruikbaar. Agile is de oplossing om debacles te voorkomen, maar waarbij de architectuur zeker belangrijk is! Ook bij de overheid wordt steeds meer Agile gewerkt, en met succes.

Interresant was te horen hoe Rabobank op deze manier werkt. Eerst hadden ze enkele langzame trage projecten van bijvoorbeeld 2 jaar. Nu hebben ze meerdere verschillende kleine teams met elk een eigen project. Bijna elke week verandert er wel iets (kleins) op www.rabobank.nl. Maar ook interne software wordt op de agile manier gedaan: bijvoorbeeld in een nieuwe versie konden ze een nieuw soort hypotheek/verzekering eerst alleen maar aanmaken, in de volgende week of nog later pas deze muteren. Dit lijkt raar, maar in de praktijk was het erg succesvol: in de 1e week waren er al 300.000 van het nieuwe product waren verkocht! Dus de 1e versie, hoe beperkt, was gelijk succesvol.
Dit agile werken houdt wel in dat de architect anders moet werken: teams werken veel sneller, maar hebben een beperkte scope. De architect heeft wel het overzicht en moet de structuur in de gaten houden. Hij moet “bij” alle teams zijn, maar kan niet “in” elk team zitten. Oftewel ook flexibel kunnen werken en wisselen en goed priotoriseren: zorgen dat niemand op jou wacht.

Om echt te profiteren van de snelle en flexibele software afdeling, moet het bedrijf in zijn geheel agile gaan werken. Want je hebt niets aan een 2 wekelijkse update als de implementatie afdeling maar per half jaar updates uitrolt. Meestal kan de QA/Test afdeling goed agile werken, en vinden de testers dit erg prettig: snelle feedback en betere samenwerking. De business en implementatie afdelingen (bovenste c.q. laagste laag) leveren meestal problemen op. Deze moeten erg wennen aan de nauwere betrokkenheid, meer en gerichte vragen, meer updates, etc.

Niet alleen software kun je agile maken, maar ook als bedrijf in het algemeen. Je kunt dan een bepaald business onderdeel of dienst agile maken, zodat je snel op marktveranderingen kunt reageren. Hierbij is het nodig dat je weet wat de barrières in je bedrijf zijn, wat de hotspots zijn (risico, belangrijk, impact, etc), welke je eventueel kunt uitbesteden, etc.

“Lean” kun je ook gebruiken: dit is echter een niveau hoger dan agile: eerst heb je bijvoorbeeld XP (coding standards, test driven development, contineous intergration, design patterns, etc), daarbovenop komt agile (als proces: korte sprints, alleen documenteren wat nodig is, etc). Lean is weer algemener en kun je gebruiken om binnen je bedrijf te kijken welke bedrijfsprocessen je kunt optimaliseren: bijvoorbeeld onderzoeken hoe lang het duurt totdat een bugmelding opgelost is, en waar de meeste tijd/vertraging in zit.

Als laatste werd een reallife casus besproken waarbij SOA (Service Oriëntend Architechture) gebruikt werd. Deze sessie was wel praktischer, hoewel ook een beetje simpel. Gedemonstreerd werd dat je door alleen RPCs (remote procedure call) en (web)services te gebruiken je niet direct flexibeler hoeft te zijn. Belangrijk is dat de business logica op 1 plek ligt (in de service) en niet bij alle clients: als een business rule verandert dan moeten alle clients geupdate worden. Eenvoudig en logisch maar misschien juist daarom snel over het hoofd gezien?

(dit was dag 1, hopelijk de volgende keer dag 2)

vrijdag 18 november 2011

Remote Control: automated GUI test with Delphi and DUnit


Why...
For my current customer I wanted to do GUI testing too. I already made unit tests for the most important parts, but with a large code base it is impossible to test all functionality, especially when you have limited time...

Because users use the GUI to make orders and do all other stuff, I thought: why not start top-down instead of bottom-up? (I known, normally you should start bottom up, but again: I had limited time). 80% of the time they use 20% of the functionality, so it should be easy to test those parts which are mostly used. And when I click the same buttons in the GUI, a lot of stuff is automatically tested! (black box testing: you don’t know if each function is properly working, you only check the final result, which is okay: better do a coarse test than nothing!).

There are several tools to do GUI automation, but I wanted to program all stuff in Delphi and with DUnit: we have a good framework with a nice data tier and business tier, so it would be a lot easier to use the existing stuff to create for example an order with order lines, check in the database if the order is created with the correct values, and check if the new order is loaded in the GUI, press some buttons, and check the database again.

And also important: I do not want to “pollute” all applications with all DUnit stuff, and I want to test the GUI app “as is”: the same .exe the customer will use.

The idea...
So I started thinking:
  • we have RemObjects SDK, so I can very easy make a remote call from DUnitTesting.exe to our Client.exe (and start it with a command line option to enable the new remote control)
  • we have Delphi 2010 with new RTTI, so I can very easy invoke a function with an array of parameters etc.
  • we have HitXML (open source edition), so I can make a simple object tree with properties to sub objects, which are automatically created by HitXML, and I can save/load the whole object with XML. By using the same technique (auto create subobjets using rtti) I can name each object with its property name, and make a back link of the child to the parent. This way I can do a reverse lookup to retrieve a full object path.
Okay, maybe I can create a “lightweight remote interface proxy facade” thingy with HitXML, using the same object path and same component names as the client, so I can search for a button by using “TComponent.FindComponent”? And send the full path with RemObjects, including an array of parameters, and use the new RTTI to execute a function by name in the remote client?
Should be possible!

The result...
After some testing and hacking, I have the following proof of concept (stripped version of what we use internally). By the way: I replaced RemObjects SDK with a simple Indy TCP call because not everybody has RemObjects (but with RO it is easier :-) ).
I made the following simple objects, with the same name and classnames as the real ones in the remote client (a bit simplified for this blog):
 {$METHODINFO ON}    
 TBaseRemoteObject = class
   property Owner: TBaseRemoteObject read FOwner;
   property Name : string                           read FName write FName;
 end;

 TApplication = class(TBaseRemoteObject)
 published
   frmMain : TfrmMain;
   frmTest : TfrmTest;
 end;

 TForm   = class(TBaseRemoteObject);
 TFrame = class(TBaseRemoteFrame);

 TfrmMain = class(TForm)
 published
   btnShowModal: TButton;
 end;

 TfrmTest = class(TForm)
 published
   btnOk: TButton;
   btnCancel: TButton;
   framTest1: TframTest;
 end;

 TframTest = class(TFrame )
 published
   Button1: TButton;
   Edit1: TEdit;
 end;

 TButton = class(TBaseRemoteObject)
 public
   procedure Click;
 end;

 TEdit = class(TBaseRemoteObject)
 public
   property  Text : string  read GetText  write SetText;
 end;
In DUnitTesting I can now make the following calls:
RemoteApp.frmMain.btnShowModal.Click;    //create modal form frmTest RemoteApp.frmTest.framTest1.Edit1.Text := 'test';    //set textCheckEqualsString( RemoteApp.frmTest.framTest1.Edit1.Text, 'test' );  //check text
This would result in clicking “btnShowModal” of the main form, which will do a .ShowModal of “frmTest” (this is programmed in the remote client). All forms are normally accessible via the “Forms.Application” object or “Screen.Forms” array, so we can find “frmTest” in the remote root (RemoteApp). In this form, we have a frame “framTest1” with an editbox “Edit1”. I made a “Text” property which does a remote call in “SetText” and “GetText” to read and write the text:
procedure TEdit.SetText(const Value: string);
begin
 TRemoteControlExecutor.Execute(Self, 'Text', [Value]);   //set “text” property with value
end;

function TEdit.GetText: string;
begin
 Result := TRemoteControlExecutor.Execute(Self, 'Text', []).AsString;
end;
How does it work...
An explanation of how the reading and writing of “Edit.Text” works internally:
The “TRemoteControlExecutor” does a reverse lookup of “Self” (in this case “Edit1”) to get the full path (“frmTest.framTest1.Edit1”). This is send to the remote client, including the function or property we want to read/write (“Text”) and the parameters for it (explained below).
When the remote client receives the call, it does a “TThread.Synchronize” to the mainthread. Then it searches for the form “frmTest”: is it “Screen.ActiveForm”, or somewhere in “Screen.Forms[]” or using “Application.FindComponent()”? When found, it does a recursive lookup for “framTest1” and “Edit1”. After that, it searches for a function named “Text” in component “Edit1” using the Delphi 2010+ rtti. In this case it won’t find such a function, so it searches for a property, and yes: property “Text” does exist :-). Now, a simple trick is done to determine a read or write of a property: if we have 1 parameter it is a write, if we have no parameters it is a read. The rest is easy with the new rtti: .Invoke() for a function, and SetValue()/GetValue() for properties. The result is written and send back to the caller.
This way you can very easy control a remote client! Seems nice and straight forward to me, isn’t it?


Remarks
The TButton.Click is done using WM_LBUTTONDOWN + WM_LBUTTONUP, instead of executing the .Click function: otherwise when a .ShowModal is done we will get a “remote lock”!
I also had to do an “Application.OnIdle” check, because creating and painting can take some time, and when a remote message is executed as the first message after create, it can give some strange results.

Tips
Note: by using the same classnames, it is easy to set up to lightweight remote facade proxy classes: you can just copy the first part of the form, for example:
type
 TfrmMain = class(TForm)
   btnShowModal: TButton;
   btnShowFloating: TButton;
   procedure btnShowModalClick(Sender: TObject);
   procedure btnShowFloatingClick(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

Of course, this can automated if needed... :-)

And by the way: by using “Application.OnMessage” and “Application.OnActionExecute” I can record the clicked buttons and actions (WM_LBUTTONDOWN, find control under mouse, etc). Not included in this demo, but can post another demo if needed?

I extended the XMLTestRunner.pas with the duration of each test, very handy to see how long each test functions takes!

With TCP you can also control multiple (infinite?) clients in a whole network, to do a “massive” test :-).


Reactions and comments...
See the code and try it yourself! What do you think about it? A good way to do GUI testing or are there better ways? Please let me know!
http://code.google.com/p/asmprofiler/source/browse/#svn%2Ftrunk%2F-Other-%2FRemoteControl