lørdag den 29. september 2007

Snotforkælet...

Prøv lige at kaste et blik på denne blog.

Der er to andre interessante observationer, som jeg også gerne vil byde ind med:
  • Nutidens pensionister har ikke betalt ind til deres egen pension via de mange penge, som de har betalt i skat i tidens løb. Faktisk har de kun betalt omkring 1/4-del af de nødvendige penge. Grunden til at forretningen løber rundt nu er dels, at fortidens pensionister var færre, billigere og levede kortere (og sådan bliver det ikke ved med at være), samt at kagen som vi deler i dag er væsentligt større end dengang (fair nok, men det gør ikke påstanden om, at de nu lever af egne "opsparede" penge mere rigtig...).
  • Den såkaldte "danske model" sikrer ikke de svage på arbejdsmarkedet. Faktisk er den skruet sammen på en måde, så de ca. 3/4 som sidder på flæsket er i stand til at holde den resterende 1/4 ude, så de ikke trykker lønner. Når arbejdsløsheden stiger eller falder, så er det stort set et spørgsmål om, at den marginaliserede 1/4 er arbejdsløse i længere eller kortere perioder (se f.eks. de såkaldte vismandsrapporter - og specielt rapporten fra juni 1988). Ikke særligt solidarisk.
Man hytter sig selv og sine...

Svamp

Jeg har fået svamp! Ja, ikke den slags svamp - en der vokser ude i haven. Vi fandt den faktisk allerede for et par dage siden, men nu havde vi tid til at kigge ordentlig på den.

Umiddelbart ser den ret uskyldig ud, men da jeg gik min svampenøgle (Politikkens visuelle svampebog) igennem, da havnede jeg på Snehvid fluesvamp.

Om den står der flg.:
En af vore få dødeligt giftige svampe. Giftstoffet er en cellegift som ødelægger lever og nyre.
Nå, da! Jeg er nu ikke sikker på, at det er en fluesvamp, for den voksede ikke som forventet i nærheden af træer, den lugtede faktisk ikke sødligt-vammel men derimod mere af champignon, og så kunne jeg ikke finde de hatskæl, som man ellers skulle forvente.

Svampebogen forudsætter med flg. passus om forvekslingsmuligheder:
Bliver forvekslet med champignoner, men disse har farvede (først lyserøde siden brune) lameller, og mange af dem vokser ofte på åbent land uden træer i nærheden. Fluesvampen vokser altid i nærheden af træer. Unge eksemplarer af champignoner kan have hvide lameller og i så fald skal man lade dem stå, indtil man er så øvet, så man uden tøven kan kende dem fra hinanden.
Jeg er derfor ret sikker på, at det er en champignon, som jeg her har fundet. Men lamellerne var hvide, og kald mig bare en tøsedreng, for jeg følger svampebogens råd og smider den ud.

...jeg vil dog godt indrømme, at jeg er lidt skuffet - det kunne have været sjovt, hvis der havde været en dødeligt giftig svamp i min have!

fredag den 28. september 2007

Nomen est omen

I vores branche elsker vi, når navnet siger noget om indholdet. Sigende navne kalder vi det, og det gør der muligt at danne sig et overblik uden at skulle gennemgå alle detajler.

Men hvad med det modsatte? Vildledende navne må de vel kaldes. Det morsomme er her, at når et navn er vildledende, så er det som oftest ikke direkte absurd, men siger derimod noget om, hvad indholdet burde have været. Et par hurtige eksempler:
  • Hjemmelavet marmelade fra supermarkedet
  • Reje ost (OK, der er rejer i, men ikke så mange som man skulle tro)
  • Socialistisk Arbejder Parti
  • Den Tyske Demokratiske Republik.
... og find selv på flere.

Nu er der kommet en ny til listen: Microsoft Permissive License (Ms-PL). Microsoft har sendt licensen til review hos OSI - og foreløbigt fået den besked tilbage, at enten gør de licensen permissive eller også finder de på et andet navn (se f.eks. her eller her eller her).

Hvad synes jeg så? Jo, jeg kan jo dels godt lide, når der er rav i den, og så synes jeg faktisk at det er fint at der er folk med rimelige principper, som også står fast på dem.

PS: Lyder "Microsoft Limited Permissive License (Ms-LPL)" ikke som et bedre navn? Synd at det allerede er taget.

torsdag den 27. september 2007

Detailplaner og scrum

I denne uge havde jeg den store fornøjelse at deltage i JAOO konferencen. Det er svært at finde et foredrag på konferencen, hvor man ikke går der derfra med noget at tænke over.

Et af de mange foredrag som jeg hørte, var om scrum (jeg tror at det var Hubert Smiths Planning in Large Companies), og der var der på en af slidene et billede af tavlen fra et sprintplanlægningsmøde. Tavlen var relativt velordnet, med store sedler i en farve til de stories som teamet havde taget ind i sprintet - og under hver story-seddel, en søjle med fine sedler i en anden farve, med de aktiviteter som teamet havde fundet frem til. Så langt, så godt.

Men så var det, at alle deltagere i teamet, ud for hver seddel, havde noteret, hvor lang tid de regnede med at skulle bruge på de enkelte opgaver i dage - og disse tider var, med initialer, overført til sedlerne. Det undrede mig, for i det scrumteam, hvor jeg p.t. deltager, er vi gået væk fra have tider og initialer på opgaverne. Jeg var ikke den eneste som undrede mig, for et af spørgsmålene fra salen var netop til disse tider.

Svaret var, at det var et forsøg på at sikre, at der var en nogenlunde ensartet belastning på alle deltagere i teamet - eller med andre ord en slags ressource leveling. Det er klart at man bør undgå en al for stor forskel imellem opgavernes karakter og teamets kompetencer, så intentionen er god nok (vi har også været præcis der).

Der var flere grunde til, at vi gik væk fra det:
  • Når opgaverne allerede er estimeret på storyniveau, så tilføjer det umiddelbart ikke nævneværdig værdi at nedbryde dette estimat på delopgaver. Der kan være en fordel i at kunne måle fremdriften, men det introducerer et administrativt overhead. Hvad værre er, så åbner det en breche overfor andre om, hvorvidt det giver vil give mening at gøre en story halvt eller 3/4-dele færdig for at kunne klemme en anden story ind - and it ain't over 'til the fat lady sings!
  • Hvis man under sprintplanlægningen allerede fordeler opgaver på folk, så er der en kedelig tendens til, at denne plan hænger ved - det bliver med andre ord også de folk, som kommer til at lave opgaverne. Man kan sige, at forskellen på papiret ikke er stor, men rent mentalt så er man ikke et team længere, hvis der på forhånd er dine og mine opgaver - den største fordel ved at team går fløjten, hvis opgaverne ikke er "vores opgaver"!
Efter min mening, så er der også andre udfordringer ved at forsøge på at lave ressource leveling, som nok ikke umiddelbart ødelægger sprintet, men som man alligevel skal have for øje:
  • Man kan nemt i forsøget på millimeterretfærdighed komme til at overse de dynamiske effekter. Det kan godt være, at der er ligeligt med opgaver til alle, men hjælper ikke noget, hvis der er nogle, som kun har opgaver som naturligt ligger først ... eller til sidst. I et godt team vil teamet arbejde udenom udfordringen, men værdien af forsøget på ressource leveling er gået fløjten.
  • Den anden fælde er, at man begynder at tilpasse opgavesammensætningen til teamet kompetencer og ikke omvendt. Teamet skal kunne levere det som giver værdi, og ikke det som det tilfældigvis er bedst til. Det tager selvfølgelig tid at få et godt team op at stå, så for en tid kan det give mening at lade opgaverne tilpasse sig teamet i nogen udstrækning - og på samme måde er det jo ideelt, hvis teamet kan opsøge opgaver, som passer til kompetanceprofilen. Det er bare vigtigt, at der ikke bliver byttet om på årsag og virkning.
Nu kunne man så sikkert godt forledes til at tro, at jeg taler for, at man helt skal ignorere, i hvor høj grad de enkelte deltager kan byde ind i et sprint - eller for at man helt ignorerer opgavesammensætningen. Det gør jeg så langt fra. Det er et vigtigt input til planlægningen at vide, hvordan teamet er sammensat - og det er naivt at ignorere effekten af f.eks. ferier og uddannelse.

Det som er vigtigt er, at teamet kan tro på at planen er realistisk, og hvis det indebærer, at teamet under sprintplanlægning vurderer på delopgaver og mulige ressourcer til dette, så er det helt fint med mig. Det som jeg hæver en solid pegefinger overfor er, dels at give det mere vægt end som input til en "mavefornemmelse" og dels at tage detailplanen med ud fra sprintplanlægningsmødet. Detailplaner er nok nyttige, men også yderst farlige, og skal derfor holdes i meget kort snor - ellers risikerer man at ødelægge mere end man gavner.

søndag den 23. september 2007

Kolonnebaserede databaser

Der er kommet en blog, som lover at snakke om databaseteknologi og innovation. Det lyder lovende, selvom det indtil videre hovedsageligt har været en platform at promovere et nystartet firma, hvis eksistensberettigelse er at de vil implementere en kolonnebaseret database (column database).

Jeg holder nu øje med den, for når man samler visionære og velformulerede folk, så vil der ofte falde noget af, som man kan blive klogere af. For mig var det da også her, at jeg første gang hørte begrebet "kolonnebaserede databaser".

Og hvad er en kolonnebaseret database? Egentlig er det i udgangspunktet meget simpelt: det er almindeligt for databasesystemer at lagre tuplerne i en relation som fortløbende rækker i en tabel - en kolonnebaseret database derimod vil lagre attributterne hver for sig i hver sin tabel, og så genskabe tuplerne ved attributterne i en tupel alle har samme position i de respektive tabeller.

Den første iagttagelse man kan gøre sig, er at det ikke ligefrem er noget stort. Hvordan man fysisk organiserer sin storagestruktur er vel en implementationsdetajle, som ingen databruger i princippet bør bekymre sig om, på linje med f.eks. indekser og matrialiserede views - og alle eksisterende relationelle databaser ville da også kunne tilbyde kolonnebaseret storagestruktur uden at skulle ændre funktionalitet udadtil.

Interessant er det alligevel, fordi der i hvertfald i teorien, burde være nogle solide performancegevinster at hente i en række almindelige brugsmønstre. Det ser umiddelbart ud til, at gevinsterne er størst for databaser, hvor læsninger er meget mere hyppige end opdateringer (hvad dels er tilfældet for mange databaser, men selv i opdateringstunge databaser er der ofte relativt statiske hjørner af schemaet).

I praksis så har tabeller en tendens til at knobskyde voldsomt over tid (og jo større datamængderne er, jo mere modstand er der imod at refaktorere, så der er ofte tale om en ond spiral...). Men for at føje spot til skade, så er der folk som insisterer på at skrive select * from ... i andet end ad hoc queries - og de burde indkaldes til en kammeratlig samtale med kodepolitiet, og i gentagelsestilfælde fratages deres kodelicens! Ydermere så ser man ofte mapningsværktøjer (f.eks. ORM-værktøjer), som insisterer på altid at læse hele tuplen.

Imod tåbelige brugere eller værktøjer, er kolonnebaserede databaser også magtesløse, men de kan give en umiddelbar gevinst ved dels, at man under dannelse af udtrækket (join og restriktion), og dels i realiseringen af selve resultatet (selektionen) ikke behøver at læse fulde records indeholdende alle felterne i hver tupel, men kan nøjes med at læse de attributter, som er relevante. Det er tilsvarende en queryoptimization, hvor man nøjes med at læse i indekses, hvis man kan finde et som indeholder alle de ønskede felter, i stedet for at læse i tabellen - også selvom den orden som indekset implicerer ikke er relevant.

En anden oplagt fordel med at opdele i kolonner er, at hver tabel har sit eget simple domæne, og at der derfor er væsentlig mindre spredning i værdierne. Indholdet lader sig derfor nemmere komprimere, og med læsehastigheden på eksterne lagringsmedier som en flaskehals, så vil det kunne være en fordel (der er langt mindre spredning i f.eks. folks efternavne end der i en fuld adresse).

Hvis man ydermere kan antage noget som orden i ens domæne eller såmænd bare i det typiske brugsmønster, så kan man sortere data efter dette. Dette vil give langt større muligheder for komprimering - i stedet for at liste alle som hedder "Nielsen" enkeltvist, så kan man nøjes med notere sig, hvor mange der er af dem, hvis man har data sorteret efter efternavn. Selv hvis man først sorterer efter by og derefter efter efternavn, så vil der stadig være mange med samme efternavn i hver by.

På den anden side, så vil kendte implementationer af relationelle databaser i dag allerede støtte flere typiske brugsmønstre med f.eks. indekser og matrialiserede views. Den store fordel ved at være kolonnebaseret vil muligvis kunne høstes ved, at man kan afvikle sine queries direkte på de komprimerede data, så man helt undgår overheadet med at skulle dekomprimere førend resultatet skal realiseres. At kunne arbejde direkte på komprimerede data kan også medføre mere effektiv håndtering af queries, f.eks. fordi at en cache af komprimerede data kan indholde større mængde af data end en tilsvarende for ukomprimerede data.

Så - for at summere op - selvom kolonnebaserede databaser ikke ved første øjekast ser ud til at være noget stort, så sker der interessante ting ved at dreje ens synsvinkel på lagringen af data 90 grader. Specielt i betragtning af, hvor ofte query-optimering i praksis går ud på at eliminere full tablescans...

torsdag den 20. september 2007

lorem ipsum

Hvis man har arbejdet med layout - det være sig GUI eller f.eks. på papir - så er der meget stor sandsynlighed for, at man har mødt noget, som ligner flg. tekst:
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Det ligner latin, og så ikke helt alligevel - og sandsynligvis så er det ment som en vrøvletekst, så man kan vurdere layout, som f.eks. valg af skrifttyper, ombydning el.lign., uden at blive forstyrret af indhold. Teksten har været en del af vores branche fra første gang, hvor vi begyndte at bekymre os om layout (alle der ved, hvad en leporelloliste er, rækker hånden op...!).

Men faktisk går rødderne sandsynligvis længere tilbage. En trykker lavede i år 1500 (eller der omkring) et ark med eksempler på skrifttyper med denne tekst, og noget tyder på, at han er blevet inspireret af ingen ringere end Cicero. Cicero skrev en menneskealder eller to efter Kristi fødsel et essay om "Godt og ondt" og i det siger han bl.a.:
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, ...
Nu skal jeg ikke foregive at kunne latin, så jeg vil straks indrømme, at jeg har kigget forskellige kilder, og tør derfor godt vove det ene øje, når jeg nu påstår at det kan oversættes med:
Der er ingen som elsker eller søger eller ønsker smerte for smertens egen skyld...
Nu er Cicero jo ikke en person, som man bare sådan lige skal affeje, men der er nu nogen, som mener, at her tager han fejl. Personlig sidder jeg og overvejer hvorfor man dengang for 500 år siden ikke har valgt sekvensen ved siden af, som snakker om glæde? Måske var det en godt skjult kommentar om, hvad han mente om kunder - og i så fald, så er der jo intet nyt under solen...

onsdag den 19. september 2007

Billeder på nettet

Det er underligt, som udviklingen kommer i spring. I lang tid kan et område stå stille, og så pludselig, så sker der tigerspring, som vender op og ned på alt hvad man har ellers har taget for givet. Og et spring kommer sjældent alene.

Grafik eller billeder på nettet har egentlig i lang tid været statiske klumper, der kunne bruges som illustration - som små malerier med smukke guldrammer, som vi kunne hænge på vores hjemmesider. Men de har også været temmeligt besværlige, for de har en normal størrelse (og det er som om, at den sjældent helt passer). Man kan selvfølgelig skalere billeder (så passer de til gengæld oftest kun på den ene led...). Sådan er det bare, ikk'?

Ja, sådan troede jeg at det var, indtil jeg så denne videopræsentation af en teknik, som hedder Seam carving.

Grundtanken er, at man finder den sti igennem billedet, som bidrager mindst til det (der er næsten altid noget blå himmel eller en ensfarvet væg), og så fjerner man en række pixels der. Eller sætter en ny række ind der. For det er der det mindst kan ses. Kunsten er "bare" at finde den sti.

Det er oplagt, at der er en del fotografer, som vil få et veritabelt føl på tværs, når de ser hvad man kan gøre med deres billeder. Specielt de purister, som mener, at billeder er objektive og at enhver form for efterbehandling er manipulation grænsende til forfalskning. Jeg venter bare på ramaskriget!

Seam carving er et forslag til at løse skaleringsproblemerne, men der er jo grænser for hvor meget man kan forstørre eller formindske. Eller er der?

Prøv så at tage et kig på teknologien PhotoSynth, som foreligger i demoform med ophav hos MicroSoft og University of Washington (der er tre gode links videre fra artiklen).

PhotoSynth er vel nærmest panoramaer på sterioder! Man tager en samling af mere eller mindre relaterede billeder og så finder man overlap. Kombiner det med metoder som gør, at man kan blænde fra et billede og over i et andet. Det gør at man kan se oversigtbillleder, og når man vil kigge nærmere på detajler, så "zoome ind" ved at skifte til et andet billede, som er taget med et mere snævert udsnit. Eller navigere til den ene side ved at skifte over til et andet billede som overlapper til den side. Og så videre.

Jeg kan huske den gamle tanke om et netværk bundet sammen af hyperlinks (jeg husker specielt HyperCard) - det her er en visual realisering af samme vision - visionen om hvordan man kan gå på opdagelse og fortabe sig i detajlerne ved at gå ud af en vilkårlig tangent - og pludselig opdage at man er kommet et helt andet sted hen end der, hvor man startede.

Tænk sig at turistbilleder på den måde pludselig kan blive værdifulde...

lørdag den 15. september 2007

God unittest?

Det er ikke så frygtelig lang tid siden, at alle talte om unittest, men ingen lavede dem. Nu er det som om, at der ikke rigtigt er nogen som taler om dem længere, men alle laver dem. Er det udtryk for, at unittests generelt har nået en modenhed, som gør, at der ikke er mere at sige om dem? Nok desværre ikke. Men hvad er det så for diskussioner som er blevet væk?

Det første gode spørgsmål, som man kan stille sig selv er, hvor stor en unit til en unittest? Der kan der siges meget klogt om, men svaret på det må være, at det er så meget som det giver mening at teste selvstændigt.

Hvor meget?

Det er oplagt, at kompleksiteten af de små enheder, er væsentligt mindre end større helheder, og at kompleksiteten stiger voldsomt selv ved små forøgelser at størrelsen. Det er derfor en god ide at starte i det små, og verificere de enkelte dele inden man går videre og verificere at man kan koble dem sammen i større og større strukturer og systemer. Ved at benytte sig af, at man kan antage, at delene allerede fungerer, så er det muligt at hæve abstraktionsniveauet på testen af helet, så man ikke så hurtigt bliver indfanget af den stigende kompleksitet. Alligevel er det oplagt, at der hurtigt bliver tale om størrelser, som det ikke giver mening at teste selvstændigt - programmer afhænger simpelthen er for mange faktorer som kun dårligt lader sig kontrollere.

Med andre ord vil det give mening at have test på integrationsniveau som unittests - hvis bare man holder for øje, at det skal kunne testes selvstændigt.

Men hvor små enheder skal man så starte med at teste? Hvis man har valgt en objekt-orienteret tilgang til at strukturere ens program, så giver svaret næsten sig selv - det er objektet og dets metoder.

Min kode kan ikke unittestes...

Er de objekter, som vi opbygger vores programmer så tilstrækkeligt uafhængige til at vi kan teste dem selvstændigt? Desværre kun alt for sjældent og det skyldes som oftest at programmet trods alle gode intentioner er endt med at have en uklar ansvarsfordeling og for funktionaliteten efterhånden er blevet godt og grundigt sammenfiltret. Og som om det ikke er rigeligt med udfordringer, så er nogle programmer er nok opbygget af klasser med metoder, men uden at være spor objekt-orienterede i deres struktur - der er passive data i nogle klasser og tilstandsløs funktionalitet i andre .

Er man endda så uheldig at man står med en større mængde eksisterende kode, som aldrig rigtigt at blevet unittestet for alvor, så vil man ofte opdage at det er opbygget på en måde, som er direkte fjentlig overfor lade sig teste selvstændigt (inden man giver op, så er der dog god hjælp at hente f.eks. i Michael Feathers bog Working Efficiently with Legacy Code - her er modsvar til alle de undskyldninger som jeg i tidens løb har set for at man ikke kan unitteste, samt en lang række undskyldninger som jeg ikke havde fantasi til... læs den inden du igen siger, at du har kode som ikke kan unittestestes!).

Hvis vi vil unitteste, så tvinger det os med andre ord til at skrive kode, som kan fungere selvstændigt. Vi bliver altså nødt til at overveje ansvarsområder, interfaces, afkobling af afhængigheder (f.eks. vha. dependency injection). Og vi bliver nødt til at læse programkoden for at kunne skrive testen. Og det er faktisk her, at mange folk mener at den egentlige værdi af en unittest ligger: At den tvinger os dels til at skrive god koden, for kode der ikke kan testes er som oftest dårlig kode - og til at reviewe vores egen kode, når vi skriver testen.

Selve testen

Unittest stiller ikke kun krav til den kode, som skal testes - en god test opstår ikke uden videre (som mange har lært på hårde måde, og som alt for mange desværre stadig har til gode at indse).

Det første og helt indlysende krav, som man kan stille til en unittest er såmænd, at den tester noget. Jeg har set eksempler på unittest som godt nok huskede at kalde noget programkode, men derefter glemte at antage noget om effekten (bevares, helt uden værdi er det ikke, for man har da i det mindste fået testet, at koden kan formå at returnere normalt efter at være blevet kaldt...).

Et andet, og måske knapt så indlysende, krav er, at testen alene skal kunne svare på om der er en fejl eller ej. Hvis der er en fejl af den type, som man tester for, så skal testen kunne fejle. Og hvis den ikke er der, så må den ikke fejle. Uanset hvor mange gange man kalder den. Jeg har set unittest, som testede webservices ved at kalde en anden server - sjovt nok, så fejlede den hver gang at der var netværksproblemer, hvilket hver gang satte grå hår i hovedet på den stakkel som ikke kunne se, hvordan hans rettelse kunne have påvirket webservicen. Jeg har også set unittest, som baserede sig på at gemme hardkodede værdier i en fælles database - med den effekt, at man kun kunne køre en instans af testen - den stakkel som kom som nummer 2 ville se testen fejle uforklarligt.

Oplevelser som mine har sandsynligvis været ophav til flg. tommelfingerregler om unittests fra Micheal Feathers hånd: En unittest
  • må ikke bruge en database
  • må ikke bruge netværk
  • må ikke bruge filsystemet
  • skal kunne afvikles samtidigt
  • må ikke krave manuel konfiguration
Et yderligere krav (som i øvrigt bliver nemmere at opfylde, hvis man overholder ovenstående) er, at unittest skal kunne afvikles hurtigt. Her tænkes på, at testens afviklingstid ikke skal måles i sekunder, men derimod i millisekunder - jeg arbejder for tiden på et projekt med kun omkring 45% coverage og der er alligevel over 2500 tests. Det kommer hurtigt til at løbe op, hvis ikke man passer på.

Ingen af kravene er dog endegyldige - jeg har haft stor værdi af unittests, som kaldte de udtræk fra databasen, som programmet brugte - databaseschemaer er ikke en statisk størrelse over tid, og de er ofte ganske løst koblet til programmet. At opdage at tæppet er blevet trukket væk under en af en databaseændring, er mere værd end det overhead, som det introducerer i kørselstid hhv. den usikkerhed om resultatet, som det introducerer at være afhængig af at databaserserveren er altid kørende og velfungerende.

Pointen om at undgå databaseadgang er dog god nok. I stedet for at inline databasekode i forretningslogikken, hvorved man ikke kan teste sin kode uden at stable et større fixture på benene i databasen, så bør man afkoble sin forretningslogik fra databasetilgangen og teste de to ting adskilt.

Et eksempel: I stedet for at have en hel postnummertabel i databasen som en del af forudsætningen for en test af validering af adresselister, så lav en metode til at finde postnumre i databasen postnummertabel, test denne - og stub den så iøvrigt af i logikken til validering af adresser (ved f.eks. at lade stubben have et memory baseret map...). Programkoden er blevet nemmere at læse og forstå, det bliver nemmere at finde og rette evt. fejl, og testen kører en størrelsesorden eller to hurtigere! Der er kun fordele...

Kvalitetssikring af test

Er der så hjælp til at få gode unittest. Ja, da.

Et af det mest oplagte - og heldigvis også mest udbredte - værktøjer, er coveragemålinger. I stedet for at tro, at man rammer de tiltænkte dele af programkoden, så kan man prøve det, og efterfølgende se at man rent faktisk gør det (eller måske mere realistisk - at man ikke rammer det man troede). De simple udgaver måler linecoverage, og det er da også et godt udgangspunkt. Men det kan være misvisende, og derfor er et bedre mål, det som ofte kaldes branchcoverage. Branchcoverage måler ikke bare, at man rammer alle linjer, men også at man får afprøvet alle mulige veje igennem programkoden - eller med andre ord, at man kommer forbi alle beslutningspunkter.

Aktivt at bruge coveragemålinger når man udvikler en unittest, er de første gange en ganske lærerig oplevelse. Man mærker hurtigt, at man som udvikler kun er vant til at tænke i positive normaltilfælde (ja, som konstruktør i videste forstand, for jeg vil godt tage analytikere og designere med i båden, selvom de ikke skriver så mange unittest som os andre...) - man mangler næsten altid at tænke på fejlhåndtering og undtagelser.

Man kan ikke snakke om coveragemålinger uden også at snakke om coverage procenter. Hvor stor en coverage skal man have for at det er godt nok? 50%? 90%? 100%? Tja, som udgangspunkt er mere bedre, men der er et par solide forbehold:
  • Der er tale om en stigende omkostning og et faldende udbytte (der er en faldende grænseværdi). Man skal med andre ord gøre sig klart, hvor meget man vil betale for de ekstra procenter (og man laver dette for at finde eller undgå fejl - hvor sandsynlig er det at der gemmer sig en ekstra fejl i det sidste procentpoint?). Eller sat på spidsen: Kan det virkelig betale sig at jagte den sidste toString(), som kun bruges til debugning?
  • Ikke alle procentpoint er lige meget værd. En tommelfingerregel siger, at 20/80 reglen også gælder for fejl i programkode, og derfor vil jeg hellere have høj dækning på de 20% af koden som har stort fejlpotentiale end høj dækning i resten. Hvis 40% coverage dækker over 100% dækning på de farlige 20% af kodebasen og 25% dækning af resten, så er det langt bedre end 0% dækning af den kritiske del og 50% dækning af det trivielle.
  • Det er ikke nok, at ramme en kodelinje - man skal også bruge det til noget (se ovenfor).
  • Generel coverage har - specielt med dårlig opdelt og afkoblet kode - en tendens til at overvurdere effekten, da der også bliver talt "collaterals" med. Eller sagt på en anden måde, det var langt fra alle mine point i dart som kan tilskrives evne og omtanke - mange point er bare heldige forbiere!
Jager man coverage uden at tage stilling til risiko og effekt, så risikerer man at få testet det trivielle grundigt, og slet ikke det vanskelige. Hånden på hjertet, så vil der nok være en tendens til at bruge tid på de lavhængende frugter, hvis der ikke er anden motivation for at prioritere anderledes.

Synes man stadig ikke, at coverage er høj nok, så bør man kaste et blik på Guantanamo: Al kode er skyldig indtil testet uskyldig - og skyldig kode slettes! Så kan man hurtigt få coverage op. Når man nærlæser værktøjet beskrivelse, så opdager man, at det trods alt ikke er så slemt - al utestet kode antages at være kandidat til at blive slettet, så der genereres et parallelt sourcetræ, som kun indeholder testet kode - så kan man selv vurdere, om man tør erstatte sin "rigtige" programkode, og i hvilket omfang.

Folkene bag Guantanamo har faktisk en anden interessant tanke: Instrumenter produktionskoden, og slet efterfølgende de dele af programmet som ingen bruger... det vil nok kræve nogle rimelig lightweight instrumenteringer - men hvis måleperioden er lang nok, så kan man nøjes med en sandsynlighedsbaseret måling. Specielt hvis det kun skal bruges som inspirationsgrundlag (mit aktuelle projekt har "mørk kode" - kode, som kun skal bruges, hvis uheldet er ude - sådan en kode vil (forhåbentlig) aldrig blive kaldt i praksis, men er stadig yderst relevant at teste og at have med i kodebasen).

Et tilsvarende ekstremt værktøj er AshCroft: Der er stadig i høj grad tale om work-in-progress, men den nuværende version bruger en Java Security manager til at sikre, at man ikke kalder kode i mere end den ene klasse, som man ønsker at teste (plus de nødvendige stubbe). Så kan man lære det!

I en anden boldgade ligger værktøjer Jester. Tanken bag Jester er, at forsøge at teste om unittesten rent faktisk tester noget. Jester vil derfor mutere koden som testes, så der med vilje introduceres fejl. F.eks. vil den ændre if-sætninger (ved f.eks. at skrive not foran betingelsen) i den forventning, at en god unittest vil fange det som en fejl - fanger unittesten det ikke, så rapporterer Jester det som en fejl i unittesten!

Bliver vi klogere?

De fleste lærer heldigvis af deres fejl - men der er en hurtigere (og efter min mening mere behagelig) måde, og det er at lære af andres erfaringer.

Jeg har ovenfor henvist til Michael Feather bog Working Efficiently with Legacy Code - den kommer med mine varmeste anbefalinger. Han adresserer på glimrende vis alle de udfordringerne der kan være at få unittest op og stå i forbindelse med eksisterende kode, både egen og andres.

Står man som Moses foran det Røde hav, og ikke aner, hvad man skal gøre, så er en god introduktion bogen Pragmatic Unittesting - det er som alle bøger i Pragmatic Programmer serien nærmest at sammenligne med Pixie-bøger, men en gang imellem er det meget rart at få en nemt læst bog imellem hænderne, som klart tager stilling til, hvad der er vigtigt og hvad der kan ignoreres i første omgang. Har man prøvet at skrive mere end en håndfuld unittests, så er udbyttet af bogen nok temmeligt ringe (men på den anden side, den er, som tidligere sagt, meget nemt læst...).

Har man brug for noget med mere kød på (og der står jeg selv nu), så vil jeg tro at bogen xUnit Test Patterns: Refactoring Test Code ville være værd at kigge på. Det står højt på listen over bøger, som jeg skal have kigget nærmere på.