onsdag 9 augusti 2023

Do you know the difference between a Fake, a Spy, a Stub, a Dummy and a Mock when writing unit tests?

Published at last

Below is a post draft that I wrote in the summer of 2017 but for reasons I don't remember it has stayed as a draft since then. 

Now, in the summer of 2023 I'll just add Martin Fowler's summary of the test doubles in the book "xUnit Test Patterns" copied from https://martinfowler.com/bliki/TestDouble.html (which I think is a great summary) and publish it.

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an InMemoryTestDatabase is a good example).
  • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.
  • Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent.
  • Mocks are pre-programmed with expectations which form a specification of the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting.

The old draft

Do you know the differences between Fake, Stub and Mock when writing unit tests? I thought I did, and was about to write some kind of guide to use at work, but thought I'd do a little bit of research first to be sure to get the details correct. After reading on different pages I became more and more confused, until I found this page http://xunitpatterns.com/Test%20Double.html, that I think describes the types and their differences in a clear way that probably can be useful for us.

The page is on a webified version of the book xUnit Test Patterns: Refactoring Test Code that seem to be a great book that I have missed out on up until now.


When you know the differences between the types, then you might want to read more about how to choose between them according to Mark Seeman here: Mocks for Commands, Stubs for Queries

In his post Mocks Aren't Stubs, Martin Fowler also explains the difference between stubs and mocks and investigates pros and cons of the classicist and mockist way of writing your tests.


So, something that I thought I already knew and I could write about in an hour or so, ended in hours of research and a post with links... keeping it DRY :)

lördag 5 augusti 2023

Skapa en AI-modell som styr en bil i AWS DeepRacer

AWS DeepRacer

AWS DeepRacer är tjänst i Amazons moln. Med hjälp av den tjänsten kan du träna upp en maskininlärningsmodell med förstärkningsinlärning så den kan köra en bil i skala 1:18 på en virtuell eller riktig bana. Modellen kan du sedan anmäla till olika tävlingar.


Som IT-konsult på Knowit hakade jag på möjligheten att prova på DeepRacer när Knowit skapade sin egen interna tävling där deltagarna fick generöst med fria modellträningstimmar. I det här inlägget tänkte jag visa lite hur tjänsten ser ut när man skapar sin modell. I ett eventuellt framtida inlägg är min plan att visa hur det ser ut när modellen tränas.

Skapa en modell

När man loggat in i Amazons molntjänst så listas de senaste tjänsterna man använt. För att komma in på AWS DeepRacer så klickar man på den länken.



Väl inne i tjänsten så får man upp menyn i bilden nedan. För att skapa en modell så går man till "Your models".


Här listas de modeller man skapat tidigare. Eftersom jag provat mig fram en hel del så har jag en del modeller liggande redan. På mitt konto finns dock en begränsning på max sex stycken samtidiga modeller, så jag tar bort en först för att visa hur man skapar en ny med knappen "Create model".



Modellen skapas med en wizard i fem steg. Före alla steg får man lite info om träningen.


Steg 1: Val av modellnamn och bana

I första steget får du namnge modellen och om du vill även ange en beskrivning av den.


Det finns 61 banor att välja mellan att träna modellen på.



Våra tävlingar skulle till att börja med köras på tävlingsbanan från 2018, det vill säga "re:Invent 2018".



Steg 2: Val av tävlingstyp och träningsalgoritm

Det finns tre olika tävlingstyper att välja mellan, just nu var det "Time trial" som gällde.


Vid val av träningsalgoritm blir det helt plötsligt rätt hög nivå... PPO eller SAC, det sa mig ingenting. Nån av guiderna jag tittade på har jag för mig föreslog att börja med PPO, ser att alla mina modeller använder den.


Vågar man sig på att fälla ut "Hyperparameters" så blir det riktigt läskigt, men som tur var så var rådet att låta dessa vara tills man blivit varm i kläderna.


Steg 3: Definiera vad bilen kan göra, d v s dess Action space

Först får man lite info igen.


Sen ska man välja mellan "Continuous" eller "Discrete" action space.


Väljer man "Continuous" så kan man i nästa steg ange max styrvinklar och min/max hastighet.




De val man gjort visas också visuellt. Man kan även klicka direkt på en av pilarna och dra den hur man vill ha det.


Eftersom tävlingen handlar om att ta sig fortast runt banan så har jag tänkt att det är bra att utöka action space med högre hastigheter, så när jag använt "continuous" så har jag ökat max hastighet från ett till fyra.


Om man istället väljer "Discrete" så ska man i nästa steg explicit ange ALLA möjliga utfall som modellen kan ha som utdata. Nedan är förinställda värden.



Jag har testat att använda båda sorterna, men det var ett tag sen jag skapade mina modeller och minns inte om någon av dem fungerade bättre. Jag fick aldrig till läsningen av loggfilerna som skapas vid utvärderingen av modellen, men om man får det så verkar logganalysverktygen mer anpassade till  (eller kanske t o m kräva) "discrete"-varianten.


Steg 4: Välja fordon

Jag har två likvärdiga virtuella bilar, den färgglada har jag för mig att jag fick som pris för att jag ställt upp med en modell i en tävling. Det finns varianter av bilarna, iallafall av de fysiska, som har dubbla framåtriktade kameror och LIDAR.


Steg 5: Belöningsfunktionen

Det är nu man får börja koda! Här ska du, i Python, ange den belöningsfunktion som modellen ska tränas med. Modellen får in bilens tillstånd, den tar ett beslut om vad bilen ska göra härnäst och din belöningsfunktion berättar för träningsalgoritmen hur bra beslutet var utifrån bilens nya tillstånd.

Nedanstående funktion är förifylld, den kan returnera fyra olika värden beroende på hur långt från mitten bilen är placerad, ju längre bort från mitten desto lägre värde. Det vill säga, den belönar modellen när den lyckas följa mittlinjen.


Men eftersom tävlingen handlar om att köra fortast så behöver man även belöna hastigheten på nåt sätt och då behöver man mer data att titta på. Data om bilens tillstånd och även lite info om banan kommer in till belöningsmodellen som en dictionary i parametern "params". Den innehåller nedanstående värden (illustrationer och förtydliganden finns här):
  • "all_wheels_on_track"
    Boolean
    flag to indicate if the agent is on the track

  • "x"
    float
    agent's x-coordinate in meters

  • "y"
    float
    agent's y-coordinate in meters

  • "closest_objects"
    [int, int]
    zero-based indices of the two closest objects to the agent's current position of (x, y).

  • "closest_waypoints"
    [int, int]
    indices of the two nearest waypoints.

  • "distance_from_center"
    float
    distance in meters from the track center

  • "is_crashed"
    Boolean
    Boolean flag to indicate whether the agent has crashed.

  • "is_left_of_center"
    Boolean
    Flag to indicate if the agent is on the left side to the track center or not.

  • "is_offtrack"
    Boolean
    Boolean flag to indicate whether the agent has gone off track.

  • "is_reversed"
    Boolean
    flag to indicate if the agent is driving clockwise (True) or counter clockwise (False).

  • "heading"
    float
    agent's yaw in degrees

  • "objects_distance"
    [float, ]
    list of the objects' distances in meters between 0 and track_length in relation to the starting line.

  • "objects_heading"
    [float, ]
    list of the objects' headings in degrees between -180 and 180.

  • "objects_left_of_center"
    [Boolean, ]
    list of Boolean flags indicating whether elements' objects are left of the center (True) or not (False).

  • "objects_location"
    [(float, float),]
    list of object locations [(x,y), ...].

  • "objects_speed"
    [float, ]
    list of the objects' speeds in meters per second.

  • "progress"
    float
    percentage of track completed

  • "speed"
    float
    agent's speed in meters per second (m/s)

  • "steering_angle"
    float
    agent's steering angle in degrees

  • "steps"
    int
    number steps completed

  • "track_length"
    float
    track length in meters.

  • "track_width"
    float
    width of the track

  • "waypoints"
    [(float, float), ]
    list of (x,y) as milestones along the track center

Här har jag sparat de belöningsmodeller jag har försökt med. I slutet försökte jag kombinera att bilen skulle ha en bra riktning mot nästa "waypoint" i banan, att den skulle ligga centrerad på banan samt ha en hög hastighet.

När man är klar med belöningsmodellen så ska man välja hur länge modellen ska tränas. Jag tyckte det verkade som att en timme var absolut minimum för att få fram en modell som klarade sig hyfsat bra.



De vill gärna att man anmäler sin modell till en tävling när den är klar. Det körs ständigt kvalificeringstävlingar där resultatet avgörs i slutet av månaden.


Sen är det bara att klicka på "Create model" så sätts träningen av modellen igång och man får rulla tummarna eller hitta på något annat under den tid man ställt in i steget innan att träningen får ta.



Avslutning

Sådär, nu har du fått en bild av hur man skapar en modell i AWS DeepRacer. Som jag skrev i början av inlägget så ska jag försöka knåpa ihop ett till inlägg där det syns hur träningen och utvärderingen av modellen ser ut.

tisdag 4 juli 2023

Podcast-avsnitt som sammanfattar psykologisk trygghet bra

Rätt ut från utkast

Det här är ett inlägg som legat som utkast i över ett år. Det jag egentligen ville ha sagt då var just att

jag tyckte att det här avsnittet sammanfattade det (psykologisk trygghet) mycket bra och att det därför kunde vara bra att "spara" som ett blogginlägg

men genast började jag gräva i vad teal-podden och teal är och därför blev målet otydligt och inlägget aldrig klart. Nu släpper jag det som det är med det här lilla tillägget 😀

Intro

Hade nyss en fönsterputsardag och lyssnade då samtidigt på avsnitt 35 av tealpodden som handlade om psykologisk trygghet. Jag har hört, sett och läst om psykologisk trygghet från många källor nu, men jag tyckte att det här avsnittet sammanfattade det mycket bra och att det därför kunde vara bra att "spara" som ett blogginlägg.

Om Tealpodden och vad är teal?

Tealpodden hittade jag för att Ulla Osterman, upphovsmakaren till dialogverktyget Orangino Work, delade det Tealpodd-avsnitt där hon var med och pratade om sitt dialogverktyg. Tealpodden drivs av Fredrik Högström och Maria Berglund:

Vi driver Tealpodden som en hobby, vid sidan av våra vanliga jobb. Idén till Tealpodden kom under en långpromenad. Vi hade läst boken Reinventing Organizations och tyckte att den satte fingret på något vi båda känt och försökt formulera – att ett bättre, snällare och mer livstillvänt arbetsliv var möjligt. Men hur kunde vi bidra till den förändringen? Jo, vi startar en podd! En podd där vi utforskar framtidens organisationer och hur en annorlunda arbetsvardag kan se ut.

 

Fredrik Högström och Maria Berglund
https://www.tealpodden.se/om-oss/

Teal är en färg mellan blå och grön och har fått sitt namn från det engelska ordet för fågeln kricka, som har färgen som ett band bakom ögat. Det var standardfärgen på skrivbordet i Windows 95.

https://commons.wikimedia.org/wiki/File:Corhwyaden.jpg

Fredric Laloux, författaren till Reinventing Organizations, kopplar färger till organisationers mognadsstadier och använder teal till det högsta stadiet. 

https://prominda.com/english/teal-organizations/

Om podcast-avsnittets gäster

I avsnittet om psykologisk trygghet så gästas Tealpodden av Maria Wigenius och Lina Hedin från Add Insight som arbetar med att bygga psykologisk trygghet i team och organisationer.

Maria Wigenius och Lina Hedin
https://www.tealpodden.se/podcast/avsnitt35/

onsdag 28 juni 2023

Gästat annat team för en Orangino Work-session

Bakgrund

Torsdagen den 15:e juni 2023 blev dagen då jag för första gången höll i en Orangino Work-session i ett annat team än mitt eget. Anledningen till att jag skriver det här inlägget är för att senare komma ihåg hur det var och tankar kring vad jag kan ändra på till en eventuell nästa gång. Om du som läser det här inte har koll på vad Orangino Work är så är det nog svårt att hänga med, men du vill kan du läsa min intro här.

Teamet som bjöd in mig är ett "kusinteam" till det jag ingår i och hade tidigare kört en omgång själva efter en mycket kort överlämning mellan mig och teamledaren. Anledningen till att jag blev inbjuden var för att teamledaren skulle kunna fokusera helt på att bara vara deltagare och för att se om det var några signifikanta skillnader i hur vi leder en session. Det här var en del av deras återblick (retrospective) de körde den eftermiddagen.

Upplägg

Jag körde ett tvåtimmarspass med en paus i mitten. Inledningsvis höll jag en kort Powerpoint-dragning där jag presenterde mig själv och varför jag var där. Berättade också kort om psykologisk trygghet och Googles studie Aristoteles. Jag drog även historiken om hur Orangino Work kom till.

Sen fick de göra en incheckningsövning, laget runt: "Berätta om någon feedback du gett eller fått sen förra tillbakablicken."

Efter det drog jag reglerna och tog upp saker vi själva funderat lite på: 
  • "Hur ska jag tänka kring skattningen för teamet? Är det hur vi är överlag inom teamet, eller är det hur teamet är mot andra team?"
  • "Jag förstår inte beteendet eller beskrivningen riktigt, vad ska jag göra?"
I stort sett så är svaret på båda frågorna: Välj att tolka det som du vill och beskriv din tolkning i samband med att du motiverar din skattning. Men ofta när det är ett krångligt ord så brukar vi snabbt försöka få en samsyn innan skattning så att vi skattar på en hyfsat lika tolkning. Sen började "spelet".

Borden var arrangerade i hästskoform, där jag satt i ena änden. Åtta personer var med, två i teamet var frånvarande. Vi hann med fem beteendekort som jag hade valt ut innan. Eftersom det finns så många kort att välja bland - vilket gör väljandet svårt - så valde jag bara från de kort som mitt team kört innan och varit givande.
  • Ber om hjälp
    Jag vågar be andra om hjälp i mitt arbete när så behövs
  • Lyhörd
    Jag är bra på att uppfatta även sådant som inte är tydligt uttalat.
  • Pålitlig
    Jag står för de utfästelser jag gör.
  • Nätverkare
    Jag bygger ständigt mitt eget nätverk som ett led i att skapa nya möjligheter.
  • Läraktig
    Jag har vilja och förmåga att lära mig nya saker.
Ordningsföljden för att motivera sin skattning körde vi laget runt medsols. Oftast frågade jag om det var nån som var frivillig att börja.

I slutet körde jag en utcheckning laget runt: 
  • "Vad har du lärt dig under sessionen?" 
  • "Har du nån feedback till mig att tänka på om jag gör det här igen?"
Stämningen var på samma gång nervös, uppsluppen och nyfiken.

Feedback från deltagarna

Det här är den feedback jag fick från insamlingen i slutet.

Bra längd
2 timmar inklusive paus upplevdes som en bra längd.

Rättvisare fördelning av att inleda en runda
Ett förslag var att vara bättre på att rotera vem det är som inleder en runda av skattningsmotiveringar. När man frågar om någon är frivillig så blir det ofta samma fåtal personer som svarar "ja" på den frågan och ofta kan det vara teamledaren. Jag tolkade personen som gav feedbacken att en runda kan upplevas olika beroende på var i ordningen man svarar. Även fast man satt sin egen skattning innan så kan de som motiverar sin skattning tidigt till viss del färga motiveringar från personer som kommer senare.

Kan fortsätta själva
Jag frågade om de tyckte det var nån skillnad mellan den här gången när jag ledde och gången då teamledaren lett sessionen. Skillnaderna de tog upp var att jag hade kört introt med psykologisk trygghet, bakgrunden till Orangino Work och incheckning. Själva sessionen verkade annars vara lika, vilket tyder på att de kan fortsätta köra på egen hand.

Egna reflektioner

Begreppet "Psykologisk trygghet" fortfarande inte välkänt
Jag tog med lite kort om psykologisk trygghet i presentationen, vad det är och hur viktigt studien Aristoteles kommit fram till att det är. Eftersom jag ser och hör om det här i väldigt många av de flöden jag följer så trodde jag att det här numera var välkänt och hade med det mest för att se om vi hade samma bild av det. Studien Aristoteles, som jag kallade "sönderrefererad", hade de inte alls koll på och hade nog heller inte nån bättre koll på begreppet psykologisk trygghet.

Så den här delen hade kunnat förlängts aningen, till exempel med Amy Edmondsons studie om sjukhusteam där de mest framgångsrika teamen också var det som såg ut att göra mest fel, fast i slutänden visade det sig att den egentliga skillnaden var att de tordes rapportera felen, som gjorde att de kunde förbättra sig.

Kanske även ta med flygplansolyckan där en av orsakerna tros ha varit att piloten med lägre rang inte tillräckligt ifrågasatte vad piloten med högre rang gjorde.

Formation och själv vara inne eller utanför
Teamet satt vid bord arrangerade i en hästsko-form, det var bra så länge jag presenterade. Men efter det skulle man kanske tightat till det lite, det hade nog varit snabbt och enkelt att dra ihop borden till en triangel. Då hade de kommit lite närmare varandra, det hade blivit lättare att höra och se varandra. Det var förmodligen inget problem, men allt som ökar kontakten borde vara bra.

Jag satt på ena ytterkanten. Min upplevelese var att man rätt ofta vände sig till mig när man motiverade sin skattning, fast det viktiga egentligen är kontakten med det egna teamet. Jag har också tänkt på att när man som teammedlem sitter fokuserat lyssnande och håller tillbaka feedback för att man inte vill påverka den som talar åt något håll kanske blir lite snål även med "ja, jag lyssnar på dig"-feedback. Det blir då lätt att ledaren tydligast visar den feedbacken och det leder i sin tur att den som talar vänder sig till denne.
Det man kan göra annorlunda är att som ledare istället stå upp utanför ringen. Då får man bättre överblick över skattningarna och de som sitter kanske får en starkare känsla av att de vänder sig till varandra och inte till ledaren. Det här får jag kolla upp.

Angående feedback "Rättvisare fördelning av att inleda en runda"
När jag gick kursen och kursdeltagarna "provspelade" så tror jag ledaren ofta valde vem som skulle börja utifrån hur man skattat och då fick de med "extremerna" börja. Det vill säga ledaren valde först någon av de som skattat högst och efter det nån av de som skattat lägst. Jag kanske ska reda lite i om det fanns nån djupare tanke bakom det, eller om det var nån tillfällig idé. För annars tycker jag det är smidigt att bara gå laget runt, det flyter på bra och alla vet när det är deras tur. Man skulle kunna skifta och köra motsols varannan gång eller nåt sånt för att fortfarande hålla det enkelt men ändå få lite mer variation.

Gör om "Om mig" mer som en historia
Delen "Om mig" i presentationen tänker jag fortfarande ska hållas mest som en parentes, men den skulle kunna göras mer i "story telling"-stil istället för ett bunt stolpar. Även om jag hade det med så kanske det kan förtydligas hur och varför jag kommit in på Orangino Work.

Incheckningen
Jag var lite fundersam över hur jag skulle göra med incheckningen. På en vanlig retro så används incheckningen delvis för att alla ska ha fått komma till tals nån gång i början, som ett sätt att komma igång så att man lättare är med och bidrar muntligt även senare. I en Orangino-session så går ordet runt oavsett, så incheckningen tappar då sin roll som munlädersmörjare. Att jag ändå tog med just "har du gett/fått nån feedback" är för att det kan vara bra att reflektera över teamets feedback-kultur, feedback är viktigt för ett bra samarbete, bland annat lär man sig vad andra uppskattar och varför och är tätt knutet till psykologisk trygghet.

Hur hantera reflektionsdelen i slutet?
Förutom att de fick köra en utchecknings-runda så skippade jag reflektionsdelen som ska göras i slutet. Jag tycker att materialet från kursen angående den är rätt svagt och den tar ganska lång tid att genomföra. Reflektionsdelen handlar om att titta på de beteenden man avhandlat och (först i par, sen i grupp) reflektera över 
  • beteendets betydelse för gruppen, 
  • vilka beteenden där gruppen har störst/minst samstämmighet,
  • vilka beteenden som väckte störst engagemang och energi
Det handlar också om en analys av hur samtalet i sig gått till, deltagarna ska reflektera över
  • Kom alla till tals lika mycket?
  • Hur var öppenheten?
  • Lyssnade vi på varandra?
Att reflektera över hur gruppen samtalat tänker jag nu ändå är en viktig sak att prata om och det vore nog bra att få in på något sätt. Så det är nåt att tänka på till nästa gång, samt även över ledarens ansvar för att styra detta. Hur mycket taltid varje person tar sig beror väl till stor del på hur man är som person. Men ska ledaren kanske försöka korta ner de mer verbala och på nåt sätt underlätta de mindre verbala att få mer tid? Eller så låter man som ledare bli det och låter deltagarna reflektera över skillnaderna så kanske de justerar det själva vid nästa tillfälle?

Sammanfatta insamlade idéer
Under sessionen skrevs det ner några idéer på saker att gå vidare med. Det som skrevs ner togs inte upp mer under sessionen, men det hade nog varit bra att i slutet sammanfatta det som skrivits ner och kommit överens om hur det ska tas vidare.

Är det här nåt jag vill göra igen?

Ja, absolut! Det var intressant att hälsa på i ett annat team och känna på deras stämning och höra vad de tycker och tänker. Som sagt så var det ett "kusinteam" till mitt eget så mycket var på grund av det bekant och hemtamt, men skillnader finns ändå, vilket kan leda till nån slags värdefullt utbyte. Sen känns det som att när ett team väl har fått en intro till Orangino Work så kan de ta det vidare själva, men jag kan mycket väl tänka mig att köra den intron för flera team.

lördag 3 juni 2023

Började skriva en Lisp-interpretator i C#

Bakgrund

Det här inlägget ska klargöra, (för dig, men också för mig själv), varför jag började försöka skriva en Lisp-interpretator i C# och hur långt jag kommit hittills.

Mycket kort Lisp-intro

Lisp är ett gammalt programmeringsspråk, det specades 1960 och är det näst äldsta högnivåspråket som fortfarande används (Fortran är äldst). Namnet är ett sammandrag av LISt Processing. Det finns många dialekter av språket, där de mest kända är Common Lisp, Scheme, Racket och Clojure.

Nåt som gör att Lisp ser en aning egendomligt ut är att uttryck avgränsas av parenteser och att det är prefix-noterat. Att skapa en variabel och binda det till värdet av en beräkning kan i ett krystat exempel i C# se ut såhär:

int x = 1 + 2;

I Lisp skrivs det såhär:

(define x (+ 1 2))

Prefix-notationen innebär att operatorn kommer först i ett uttryck och sen dess argument. Det kan se lite avigt ut när man är van med annat. 

Varför är "list" en del av Lisps namn då? Jo, programkoden utgörs av listor, som byggs upp av enkellänkade par. Uttrycket ovan representeras internt med den här strukturen:

Hur jag kom i kontakt med Lisp

När jag började på högskolan i Linköping så möttes man, som jag minns det, av en mängd mattekurser. När man tagit sig igenom dem, så var det äntligen dags att programmera! Men mycket snart upptäckte vi att det skulle göras i gamla dammiga Common Lisp 😱 När det fanns C++ och Java?!? Men, det var nog vettigt, det gav förståelse för att programmeringsspråk är verktyg som har olika styrkor och svagheter och att inte fastna för mycket i ett enda. Jag kan dock inte påstå att jag har använt just Lisp utanför skolan än.

Så, varför skriva en egen interpretator?
Ja, varför? Det har säkert gjorts massor av gånger förr, så det finns förmodligen absolut noll behov av en till. Det var nog mest bara för att se om jag kan. Och dels för att jag gäckas av att jag på flera ställen sett utvecklare referera till den gamla boken "Structure and Interpretation of Computer Programs" som nån slags holy grail. Jag hade den som kursbok på högskolan, men det känns som att jag aldrig fick nån riktig "kontakt" med den. 

Eftersom den anses så speciell så har jag några gånger på senare tid försökt läsa den, men det har inte gått att hålla intresset uppe. Andra halvan av boken handlar om att implementera en Lisp-interpretator i Lisp, en så kallad "metacircular interpreter", så jag tänkte att en ingång som skulle kunna göra boken mer intressant är att faktiskt utveckla en själv, men i C# istället. Hittills tycker jag att det fungerat bra, det har naturligt väckts en hel del frågeställningar som jag får leta reda på svaren på. Tanken var från början att göra så mycket som möjligt utan stöd från boken eller nätet för att inte bli påverkad för mycket eller få för många svar direkt så att jag skulle tappa intresset. Men jag fastnade fort och använder boken helt fritt och har även sökt på nätet en hel del, men fortfarande restriktivt för att inte bli serverad för mycket 😊


Resultat hittills
Såhär i efterhand, när jag är en bit på vägen, så hade varit lite intressant om jag skrivit nån slags notering om mina tankar efter varje sittning med detta. Men det har jag inte gjort, så det får bli en liten sammanfattning av var jag är nu och de minor jag kan minnas.

Ha alltid med ett Environment-objekt
När jag började så skapade jag bara en Evaluator-klass och skickade in Lisp-uttryck till den, till exempel addition.

var result = Eval("(+ 1 2)");

Det gick bra, till en början. Sen behövde jag spara värden på variabler, hur skulle jag hantera dem? Det hade säkert gått att spara även dem i evaluatorn, men det blev snabbt enligt bokens förslag en egen klass för omgivningen "Environment".

var env = new Environment();
var result = Eval("(define x (+ 1 2))", env);

Nu kunde variabelvärden sparas till och hämtas från Enviroment-objektet.

I samband med detta förstod jag att även funktionen "+" ska kunna sparas som ett värde på en variabel och därmed slås upp och hämtas från Environment. Då började jag få problem med att jag skickade runt uttryck som strängar och jag kunde inte spara "+"-funktionen som en sträng i Environment. 

Uppdelning i lexer, parser och evaluator
Den som skriver en "metacircular interpreter" stöter inte på problemet med hur data ska representeras, eftersom den redan arbetar med Lisp-data. Men när man skriver interpretatorn i ett eget språk så uppstår frågan hur data ska representeras. Min start där allt var strängar höll inte. Efter lite sökande på nätet stötte jag på https://amirkamil.github.io/project-scheme-parser/ som beskriver att processen kan delas upp i tre steg, lexing, parsing och evaluation.

Lexer
En lexer tar en sträng och delar upp den i tokens. Skickar man in strängen nedan i min lexer 

"(define x (+ 1 2))"

så delar den upp strängen i en lista med tokens: [(, define, x, (, +, 2, 3, ), )]


Parser
Parsern tar sedan dessa tokens och gör om dem till listobjekt. Till detta har jag klassen ListExpression, som ärver av Expression och sparar två Expression-värden internt, left och right.
public class ListExpression : Expression
{
    private readonly Expression? _left;
    private readonly Expression? _right;

    public ListExpression(Expression? left, Expression? right)
    {
        _left = left;
        _right = right;
    }

Evaluator
Nu har evaluatorn ett listobjekt att jobba med. Huvudfunktionen Eval i den ser just nu ut enligt nedan.

public Expression Eval(Expression expression, Environment env)
{
    if (IsSelfEvaluating(expression))
        return expression;

    else if (expression is VariableExpression ve)
        return env.GetValue(ve.Value);

    else if (SpecialFormAssignment.Recognises(expression))
        return SpecialFormAssignment.Evaluate(expression, this, env);

    else if (SpecialFormIf.Recognises(expression))
        return SpecialFormIf.Evaluate(expression, this, env);

    else if (SpecialFormAnd.Recognises(expression))
        return SpecialFormAnd.Evaluate(expression, this, env);

    else if (SpecialFormOr.Recognises(expression))
        return SpecialFormOr.Evaluate(expression, this, env);

    else if (expression is ListExpression list)
    {
        var evaluatedOperator = Eval(Operator(list), env);
        var evaluatedOperands = 
           EvalOperands(Operands(list), env).ToList();
        return Apply(evaluatedOperator, evaluatedOperands, env);
    }

    throw new Exception(
        $"Can not evaluate the expression '{expression}'");
}

Den hanterar hittills:
  • Självevaluerande uttryck.
    Bool och nummer, ska nog lägga till sträng-hantering med.
  • Variabeluppslag
  • Tilldelning av värde till variabel
  • If
  • Uttryck
    Just nu endast +, - och specialformerna and och or.
    (Ett vanligt uttryck evaluerar alla argument, en specialform evaluerar bara de argument som behöver evalueras.)
Så det finns en hel del kvar att göra, dels mängdgöra där stöd för flera matematiska operatorer är en del, t ex < > = * /, men det som är lite mer spännande är nog att lägga till stöd för att definiera egna metoder. Och eftersom det här inte på nåt sätt är ett försök att göra en komplett implementation av Lisp (eller egentligen Scheme) så kommer jag nog gå i riktning mot det som jag för tillfället känner mig mest nyfiken på att utforska.

Prova på eller titta på kod
Testa kan du göra genom att köra programmet som ligger här.

Plus och minus

And och or

Definiera variabler och tilldela värde

If-satser






Titta på koden kan du göra här: https://github.com/carlbjorknas/sicp-laboration
När jag skrev detta inlägg var jag på denna commit.

söndag 6 februari 2022

Gherkin - det gemensamma "språket" från produktägare till utvecklare

Gherkin

Jag har nu haft möjligheten att använda Gherkin på jobbet, ett språk, eller kanske mer en mall, som används för att skriva systemtest så att de ska bli läsbara och förståeliga hela vägen från produktägare till utvecklare. Vi är bara i början av resan, men det verkar mycket lovande och intressant! Rekommenderar!

Om du är som jag och undrar varför saker heter som de heter så kan jag avslöja att gherkin betyder "liten picklad gurka". Och språket Gherkin används i testramverket Cucumber, vilket frun till skaparen fick namnge:

My wife suggested I call it Cucumber (for no particular reason), so that's how it got its name. I also decided to give the Given-When-Then syntax a name, to separate it from the tool. That's why it's called Gherkin (a small, pickled Cucumber).


Cucumber är ett Ruby-ramverk, sitter du med .net så är det SpecFlow som gäller om du vill skriva test med Gherkin.

För att du ska få en känsla för hur språket ser ut så kommer här ett exempel, där de fetkursiva orden, samt möjligheten till den korta beskrivningen på raden under scenario, tillhör Gherkin:

Scenario: Duplicate email

Where someone tries to create an account for an email address that already exists.

Given I have chosen to sign up

But I enter an email address that has already registered

Then I should be told that the email is already registered

And I should be offered the option to recover my password


plant, fruit, food, green, produce, vegetable, fresh, kitchen, gourd, eating, vegetables, cucumber, cucurbita, cucumbers, gherkin, preparations, flowering plant, pickled cucumber, ensiling cucumbers, mizeria, land plant, cucumber gourd and melon family, summer squash, Free Images In PxHere


Godbitar jag hittat

För att komma in i testskrivandet lite snabbare så läste jag The Cucumber Book - Behaviour-Driven Development for Testers and Developers. Här nedan kommer de partier jag markerat som "kom ihåg" under läsning.




Readability

When you're writing Cucumber features, make readability your main goal. Otherwise, a reader can easily feel more like they're reading a computer program than a specification document, which is something we want you to try to avoid at all costs. After all, if your features aren't easy for nonprogrammers to read, you might as well just be writing your tests in plain old Ruby code. The real key to expressive scenarios is having a healthy vocabulary of domain language to use to express your requirements. That said, using only the basic set of Gherkin keywords can often make your features repetitive, making them cluttered and awkward to read. By the end of this chapter you'll know everything there is to know about Gherkin's syntax, giving you all the tools you need to write clear, readable Cubumber acceptance tests.

System tests Vs unit tests

It's also worth thinking about whether some of the behavior you've specified in Cucumber scenarios could be pushed down and expressed in fast unit tests instead. Teams that enthusiastically embrace Cucumber sometimes forget to write unit tests as well and rely too much on slow integration tests for feedback. Try to think of your Cucumber scenarios as broad brush strokes that communicate the general behavior of the code to the business, but still try to get as good a coverage as you can from fast unit tests. Help make this happen by having testers and programmers work in pairs when implementing Cucumber scenarios. This pair can make good decisions about whether a piece of behavior necessarily needs to be implemented in a slow end-to-end Cucumber scenario and drive out the behavior using a fast unit test instead.

Överflödiga detaljer

Nedanstående test tas upp som ett exempel på ett test som innehåller alldeles för mycket detaljer på fel nivå.

Ett scenario för en e-postklient på webben:

Scenario: Check inbox
Given a User "Dave" with password "password"
And a User "Sue" with password "secret"
And an email to "Dave" from "Sue"
When I sign in as "Dave" with password "password"
Then I should see 1 email from "Sue" in my inbox

Användarnamnen är användbara, eftersom de är viktiga beståndsdelar i scenariot, men lösenorden är bara brus, de har inget att göra med det som testas och gör testet svårare att läsa. Till exempel så har Sue ett annat lösenord än Dave, vilket kan leda till att du som läsare av testet börjar fundera över om den delen är relevant och tappar då fokus från scenariots huvudsakliga mening, dvs: att testa att Dave kan se Sues email.

Lösenorden är alltså överflödiga detaljer (incidental details), detaljer som nämns i scenariot, men som saknar relevans för scenariots uppgift. 

Här är testet utan lösenord:

Scenario: Check inbox
Given a User "Dave"
And a User "Sue"
And an email to "Dave" from "Sue"
When I sign in as "Dave"
Then I should see 1 email from "Sue" in my inbox

En klar förbättring, mer lättläst och testets betydelse framträder tydligare. Ännu mer brus kan tas bort:

Scenario: Check inbox
Given I have received an email from "Sue"
When I sign in
Then I should see 1 email from "Sue" in my inbox

Nu är det ett koncist trestegs-scenario, som också är lättare att underhålla. Om användarautentiseringen måste ändras, så påverkar det inte själva testet utan bara koden bakom. 

When Cucumbers Go Bad: Imperative Steps

In computer programming, there are two contrasting styles for expressing the instructions you give to a computer to make it do something for you. These styles are called imperative programming and declarative programming.

Imperative programming means using a sequence of commands for the computer to perform in a particular order. Ruby is an example of an imperative language: you write a program as a series of statements that Ruby runs one at a time. 
A declarative program tells the computer what it should do without prescribing precisely how to do it. CSS is an example of a declarative language: you tell the computer what the various elements on a web page should look like, and you leave it to take care of the rest.

Exempel i imperativ stil:

Scenario: Redirect user to originally requested page after logging in
Given a User "dave" exists with password "secret"
And I am not logged in
When I navigate to the home page
Then I am redirected to the login form
When I fill in "Username" with "dave"
And I fill in "Password" with "secret"
And I press "Login"
Then I should be on the home page

Enligt författaren så kan man argumentera att ovanstående testexempel har en del fördelar. Men han menar på att man snart kommer drabbas av problem med test som lätt går sönder och uttråkade intressenter, t ex produktägaren. Test som skrivs på den stilen blir brusiga, långa, tråkiga att läsa och går lätt sönder. Till exempel en så liten ändring som en omdöpning av knappen från "Login" till "Log in" får testet att gå sönder.

Men det värsta är att språket inte tillhör den egentliga problemdomänen, utan blir mer lågnivå-språk med ord som "fill in" och "press", vilka tillhör gui-domänen.

Exemplet i deklarativ stil:

Scenario: Redirect user to originally requested page after logging in
Given I am an unauthenticated User
When I attempt to view som restriced content
Then I am shown a login form
When I authenticate with valid credentials
Then I should be shown the restricted content

Författaren skriver att det snygga med detta är att testet inte kopplas till en specifik implementation av användargränssnittet. Samma scenario kan användas för en desktop-app som en mobil-app.
Orden som används (unauthenticated, restricted, credentials) är förståeliga för en intressent som håller på med säkerhet.

It's true that using declarative style will mean you have to write more step definitions, but you can keep the code in those step definitions short and easy to maintain by pushing the actual work off into helper methods in your support code.

onsdag 5 januari 2022

DevOps i 40-talets kolgruvor

Grundtankarna i DevOps inget nytt
Boken DevOps Paradox - The truth about DevOps by the people on the front line av Viktor Farcic är en samling intervjuer med framträdande personer inom DevOps-rörelsen, där de får möjlighet att beskriva hur de definierar DevOps och deras tankar runt det, eftersom DevOps inte är en väldefinierad process enligt Victor:

The thing is, if there's anything that my years of working in the field have taught me, it's that DevOps is not a well-defined process. There is no set of rules that must be followed. As I discovered in my journey, and as you'll read in these pages, it's even questionable whether there is such a thing as a "DevOps department" or a "DevOps engineer". This ambiguity is exactly why DevOps is so fascinating to me, and I hope to you, the reader, as well.
En av personerna som intervjuas är Kevin Behr, som i sin intervju lyfter att DevOps-tänket med gemensamt mål, samarbete över gränser, kontinuerligt experimenterande och lärande användes naturligt som en "överlevnadsinstinkt" dolt nere i kolgruvorna på 40-talet. Nedan är en översättning av exemplet han tar upp i intervjun ihopbakat med lite av det han tar upp i sin dragning First In Last Out - devops roots in coal mining - Kevin Behr - devopsdays Pittsburgh 2014


Sociologer till kolgruvorna
Efter andra världskriget behövde kolproduktionen i Storbritannien öka som ett led i att snabba på återuppbyggandet av landet efter all förstörelse från kriget. Det låg alltså i nationens intresse att kolgruvornas produktion maximerades. För att uppnå detta anställde regeringen två sociologer, Eric Trist och Elliott Jacques, för att de skulle undersöka gruvorna som ett led i att se vilka som var mest produktiva och vad som gjorde att de var det.

Trist och Jacques upptäckte att alla lågproducerande gruvor hade en hög grad av automatisering, men att automatiseringen inte uppnådde förväntad produktivitetshöjning.

Vad funkar bäst? Team work!
Bland de många olika typerna av gruvdrifter hittade de en design som verkligen särskiljde sig, en design som hade flera gånger högre kolproduktion än de andra. Utöver hög produktion så hade den även signifikant färre skador och en mycket stark lagkänsla!

En annan skillnad de upptäckte var att arbetarna kom till jobbet som de skulle, vilket var en aning udda, för i andra gruvor var som regel 30% av arbetsstyrkan borta. Att jobba i kolgruva var farligt och det fanns många andra jobb i efterkrigstidens Storbritannien.

För att få reda på orsaken till arbetarnas höga närvaro i de högproducerande gruvorna så pratade Trist och Jacques med arbetarna när de kom tillbaka efter sina skift, men det gav inga ledtrådar till vad som kunde vara annorlunda. Så de började följa med arbetarna ner i gruvorna.

Embed from Getty Images
circa 1955: Welsh miners at work in a coal mine.

Självorganisering
Ovan jord så samlade skiftledaren alla arbetarna och gick igenom vad de förväntades göra. Men sen, väl nere i gruvan så märkte de direkt en skillnad: gruppen demokratiserade arbetet. De fokuserade på hela uppgiften, vad de skulle åstadkomma som arbetslag, istället för "jag fokuserar på mitt så gör du ditt".

De pratade om vad som skulle göras och delade upp arbetet mellan sig på ett lämpligt sätt, t ex "Vem drack inte igår? Ok, du får hantera sprängmedlen idag." Vem håller koll på säkerheten? Vem kör borren? De var självorganiserade och självreglerade.

Samtidigt prioriterade de att lära varandra sina uppgifter. På så sätt påverkades arbetsgruppens insats mindre en dag då någon hade ont eller kanske var tankspridd på grund av nåt som hänt i familjen så att personen inte klarade av sina vanliga uppgifter. Det var heller inte helt lätt att ta sig upp ur gruvan om nån blev allvarligt skadad under arbetet, därför behövde flera känna till hur man manövrerade de fordon och maskiner som behövdes för att ta sig ut.

Pareto-principen vid kunskapsspridning
Wikipedia beskriver Paretoprincipen såhär:
Paretoprincipen är en matematisk fördelning enligt vilken 20 procent av orsakerna ofta står för 80 procent av verkan; den kallas ibland även 80/20-regeln. Vilfredo Pareto visade att 20 procent av den italienska befolkningen innehade 80 procent av egendomen och denna observation har av andra senare generaliserats.
Inom mjukvaruutveckling verkar nedanstående gälla:
  • 80% av koden kan skrivas på 20% av tiden
  • 20% av koden har 80% av buggarna
  • 20% av funktionaliteten i ett program används 80% av tiden
Kevin Behr teoretiserar att gruvarbetarna utnyttjade Paretoprincipen för sin kunskapsspridning, det vill säga "lär mig 20% av kunskapen som behövs för att utföra din upgift till 80%". 

Om att lära sig varandras uppgifter på det sättet säger han: "And that is what DevOps is!"

Sociotechnical systems (STS)
Utifrån sin forskning angående kolgruvornas effektivitet så var Eric Trist med och myntade begreppet sociotechnical systems, där sociotechnical refererar till kopplingen mellan de sociala och tekniska aspekterna av en organisation eller samhället som helhet. På grund av att de tekniska och de sociala bitarna kan gå in i varandra så mycket, så måste man vid en optimering av en process ta hänsyn till båda bitarna (teknisk utveckling och kvalitén på personernas arbetsliv). För optimerar man bara en del så riskerar oväntade effekter från den andra delen att motverka optimeringen.

I kolgruvornas fall var det att man automatiserade gruvorna, men automatiseringen ledde till mer ensamarbete och arbetarnas arbetslivskvalité sjönk, vilket ledde till sämre mående och sämre arbetsmoral. Detta tas upp i podcasten Talking About Organizations avsnitt 34 Sociotechnical Systems – Trist and Bamforth

Vad ta med sig från detta?
Ja, vad man ska dra för lärdom av detta? Kanske att om man är på gång att införa DevOps på arbetsplatsen inte stirra sig blind på processerna och verktygen för automatisering av kontinuerlig integration/installation och automatiserade tester utan även ha fokus på det sociala samarbetet?