Jag ska hålla en session på Scandinavia Developer Conference

clock december 10, 2009 20:20 by author Magnus Härlin
Jag ska hålla en session på Scandinavian Developer Conference. Det ska bli riktigt roligt. Ämnet är Design For Testability. Sen är det många andra jätteintressanta sessioner som ska hållas. Gå in och titta om det finns något som fångar erat intresse.


Automatisk uppdatering av versionsnummer i TFS bygge

clock november 18, 2009 17:53 by author Magnus Härlin

Om man har en automatiserad byggprocess i TFS och vill att versionen på alla assemblies ska uppdateras automatiskt för varje bygge kan man ta hjälp av MSBuild Community Tasks som har många bra tasks för MSBuild. I mitt exempel så tar jag och lägger in Build numret som ÅÅMM och sen tar jag changeset numret från TFSen. På så sätt är det lätt att spåra tillbaks en specifik release till en kodbas. Vill man ändra Major och Minor versionen är det bara gå in och göra det i byggscriptet.

 

För att lägga in versionshanteringen i byggprocessen går man in i projektfilen och lägger till:

 <Import Project="..\MsBuildTasks\SetVersion.targets" /> 

Pathen ska förstårs vara till den plats där man skapar SetVersion.targets filen i förhållande till projektet.

 

Har man flera projekt lägger man till referensen i alla projektfilerna som man vill ha versionen uppdaterad på. I den katalogen där man har SetVersion.targets filen behöver man även lägga in MSBuild.Community.Tasks.dll. Man får lägga till en fil som heter AssemblyVersion.cs i projekten man vill ha versionen också. Den populeras automatisk av bygget men måste vara med i projektet för att den ska komma med i kompileringen. 

 

 <Project InitialTargets="SetVersion" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">     <UsingTask AssemblyFile="MSBuild.Community.Tasks.dll" TaskName="MSBuild.Community.Tasks.AssemblyInfo" />   <UsingTask AssemblyFile="MSBuild.Community.Tasks.dll" TaskName="MSBuild.Community.Tasks.Tfs.TfsVersion" />

   <PropertyGroup>

           <Major>1</Major>

           <Minor>0</Minor>

           <Build>0</Build>

           <Revision>0</Revision>

  </PropertyGroup>

   <Target Name="SetVersion" Condition="'$(IsDesktopBuild)'=='false'">

            <TfsVersion LocalPath="$(SolutionDir)" TfsLibraryLocation="$(SolutionDir)\Dependencies">

             <Output TaskParameter="Changeset" PropertyName="Revision"/>

           </TfsVersion>

           <Time Format="yyMM">

             <Output TaskParameter="FormattedTime" PropertyName="Build" />

           </Time>

            <Message Text="%0a%0dMaking files writeable" />

           <Exec Command="attrib Properties\AssemblyVersion.cs -R" />

                      <AssemblyInfo CodeLanguage="CS" OutputFile="Properties\AssemblyVersion.cs" 

    AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"

      AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)"

             Condition="$(Revision) != '0'" />

            <Message Text="%0a%0dMaking files readonly" />

           <Exec Command="attrib Properties\AssemblyVersion.cs +R" />

   </Target>

 </Project>

 När byggservern bygger projekten så kommer den sätta variabeln $(IsDesktopBuild) till false. Lägger man inte in det conditionet kommer versionen uppdateras varje gång och det finns ingen anledning att göra det när man bygger på klienten. Skulle man jobba offline och inte ha tillgång till TFSen när man bygger får man ett exception. På det här sättet kan man bygga offline men när byggservern gör bygget så läggs versionnumret in.



S.O.L.I.D. Design Principles – Separation Of Concerns

clock februari 5, 2009 17:54 by author Magnus Härlin

Separation Of Concerns är den första principen i SOLID och innebär att en klass bara ska göra en enda sak. I mitt exempel har vi en PriceCalculator klass som räknar ut hur mycket det kostar att hyra en film. Förutom att räkna ut priset kollar den vad det är för kundtyp och filmtyp. Switch och if satsen är inte helt otydligt men det går att få klassen betydligt tydligare. Nu har den fler ansvar och skulle man lägga till en kundtyp eller en filmtyp till skulle koden börja likna spagettikod och vara jobbig att underhålla.

 

Ett anrop till klassen ser ut såhär:

 

PriceCalculator priceCalculator = new PriceCalculator();

int price = priceCalculator.CalculatePrice(CustomerType.VIP, MovieType.Normal);

 

När CalculatePrice klassen anropas med VIP kund och Normal film kommer den att skicka tillbaks ett pris på 20 kr för att VIP kunden får 5 kr i rabatt. Här nedanför är koden för att komma fram till det.

 

public enum CustomerType

{

    Normal,

    VIP

}

 

public enum MovieType

{

    Normal,

    Transfer

}

 

public class PriceCalculator

{

    public int CalculatePrice(CustomerType customerType, MovieType movieType)

    {

        switch (customerType)

        {

            case CustomerType.Normal:

                if (movieType == MovieType.Normal)

                {

                    return 25 - 0;

                }

                else if (movieType == MovieType.Transfer)

                {

                    return 35 - 0;

                }

                break;

            case CustomerType.VIP:

                if (movieType == MovieType.Normal)

                {

                    return 25 - 5;

                }

                else if (movieType == MovieType.Transfer)

                {

                    return 35 - 5;

                }

                break;

        }

 

        return 0;

    }

}

 

 

Ett första steg för att få koden mer lättläst är att ta bort CalculatePrice klassens ansvar för att kolla kundtyp, filmtyp och vad det ska vara för rabatt. Vi delar upp det i två klasser. Kundtypen används bara för att ta reda på vad det ska vara för rabatt så vi skapar en kundklass med en property för rabatt. Filmtypen används för att bestämma vad det ska vara för pris på filmen så vi skapar en klass filmklass som har en property för pris. Genom att göra det här så blir det inte så mycket kvar att göra för PriceCalculator mer än att ta priset – rabatten och returnera värdet. Slutresultatet för PriceCalculatorklassen blir alltså:

 

    public class PriceCalculator

    {

        public int CalculatePrice(Customer customer, Movie movie)

        {

          return movie.Price - customer.Rebate;

        }

    }

 

Men hur ska vi göra för att på ett tydligt sätt visa att det finns olika filmtyper som har olika pris? Ett sätt är att skapa en basklass som heter film och har ett fast pris på 25 och sen göra en underklass som ärver den klassen och gör en override på priset.

 

    public class Movie

    {

        public virtual int Price { get { return 25; } }

    }

 

    public class TransferMovie : Movie

    {

        public override int Price { get { return 35; } }

    }

 

På precis samma sätt kan vi göra med kundklassen för att kunna sätta olika rabatt.

 

    public class Customer

    {

        public virtual int Rebate { get { return 0; } }

    }

 

    public class VIPCustomer : Customer

    {

        public override int Rebate { get { return 5; } }

    }

 

För att göra ett anrop för en VIP kund på en normal film nu så skiljer det inte så mycket från tidigare. Man byter ut Enum typerna till klasserna man skapat istället bara.

 

PriceCalculator priceCalculator = new PriceCalculator();

int price = priceCalculator.CalculatePrice(new VIPCustomer(), new Movie());

 

Returvärdet man får kommer att vara 20 kr precis som tidigare.

 

Separation Of Concerns är alltid bra att ha i bakhuvudet när man kodar och börjar få nestlade if satser eller switchsatser. Ibland går det att på ett smidigt sätt bryta isär det man gör och på så sätt få kod som är lättare att underhålla och tydligare att använda.



BOM - Byte-Order Mark i början av xml dokument

clock februari 4, 2009 07:48 by author Magnus Härlin

Byte-Order Mark (BOM) används för att tala om vad det är för UTF encoding på en fil och ligger alltid som dom första teckna i filen. Har man en texfil som är har UTF encoding är det tre tecken längst fram i filen som säger vilken UTF det är. Det här är inga problem så länge man jobbar i windowsmiljö. Öppnar man filen i notepad är dom tre första teckna osynliga men tittar man med en hexeditor eller något annat kan man se dom.

 

Den potentiella risken är om man exporterar filer till andra system som får fel vid inläsning om BOM teckna sitter i början av filen.

 

Här är ett exempel på en xml fil som kommer att sparas ner med BOM i början.

 

            XElement xmlDoc = XElement.Load("xmlDocumentToLoad.xml");

 

            //Make changes to the xml Document

 

            xmlDoc.Save("updatedXmlDocument.xml");

 

För att komma runt det här problemet kan man använda en xmlWriter för att spara istället för Save funktionen. På det här sättet får vi en chans att välja om vi vill skriva ut BOM i början av filen när vi initierar Encoding klassen. Det här fungerar förstås utmärkt om det är så att man behöver använda en annan encoding än UTF också.

 

            XElement xmlDoc = XElement.Load("xmlDocumentToLoad.xml");

 

            //Make changes to the xml Document

 

            XmlWriterSettings writerSettings = new XmlWriterSettings();

            writerSettings.Encoding = new System.Text.UTF8Encoding(false);

            writerSettings.CloseOutput = true;

            writerSettings.Indent = true;

 

            XmlWriter xmlWriter = XmlWriter.Create("updatedXmlDocument.xml", writerSettings);

            xmlDoc.Save(xmlWriter);

            xmlWriter.Flush();

            xmlWriter.Close();

 

 



Pimp My Code

clock februari 2, 2009 09:06 by author Magnus Härlin

Under förra veckan gå gick Pimp My Code av stapeln i Göteborg. Det var en väldigt lyckad och intressant kväll.

 

Första sessionen höll Joakim Sundén i. Han pratade om kodkvalitén och proffesionalismen hos programmerare. Några av dom intressantaste punkterna som  han tog upp var

·         ”Broken window theory” – Om man har ett krossat fönster i ett hus och inte reparerar det kommer det leda till att flera fönster krossas och att det är större risk för inbrott eller skadegörelse. Det samma gäller för kod, om man ser många fulfixar så är det större risk att det leder till fler.

·         Förändring börjar alltid hos en själv. Om man själv har attityden att alltid checka in bättre kod än man checkade ut så är det ett bra utgångsläge.

·         Parprogrammering ger 60 % buggupptäckningsgrad jämfört med att sitta och koda själv.

·         Parprogrammering tillsammans med TDD ger ca 98 % buggupptäckningsgrad jämfört med att sitta och koda själv utan TDD.

 

Andra sessionen höll Patrik Löwendahl och den handlade om S.O.L.I.D. principerna som jag nämnt tidigare i ett tidigare inlägg. Jag kommer att lägga upp en post om varje enskild princip så jag nämner inte så mycket mer om det här.

 

Tredje sessionen höll Fredrik Normén och den handlade om Refaktorering till Mönster och exemplet var ett väldigt klassiskt på hur en swith sats lätt kan växa sig lite för stor eller riskera att göra det och hur man genom att använda Separation Of Concerns kan lösa det snyggare. Jag kommer att ta upp det i inlägget om Separation Of Concerns som är den första principen i S.O.L.I.D.

 Sista sessionen höll Dag König och den handlade om Kodkvalité och hur man kan mäta den med olika verktyg. Bland annat fxCop som man kan koppla in till Visual Studio och sen även Visual Studios egna code metrics. Med fxCop kan man välja vilka regelbibliotek man vill att den ska använda och så får man veta om koden bryter mot några av dom. Den kan vara namnstandard som inte följs eller att man bryter mot best practices. Code Metrics i VisualStudio har några olika kategorier som LinesOfCode, ClassCoupling, CyclomaticComplexity (hur många utgångar en funktion kan ha) och Maintainability Index där bra värden är < 70 men sen finns det förstås alltid undantag.


TDD Exempel

clock november 21, 2008 15:29 by author Magnus Härlin

Det här är ett kort exempel på hur man skulle kunna lägga upp testdriven utveckling av en funktion som ska ta in en sträng och parsa den och returnera ett nummer.

I TDD är det tre steg man alltid gör om och om igen tills allt är klart, stegen är:
• Skriv ett test som inte går igenom
 Ingen kod utan ett fallerande test
• Få testet att lyckas
Med så enkel kod som möjligt
• Gör koden bättre
Refaktorera

När man skriver första testet ska det vara det enklaste sättet att få funktionen att lyckas. Allra enklaste hade jag tyckt var att skicka in en tom sträng, men eftersom kravet inte sa någonting om det så blir det första jag får man först se till att kravet är tydligt genom att fråga sin chef eller kund istället för att gissa. Nu har vi fått ett tydligare krav och vet att skickar man in en tom sträng ska funktionen returnera 0.

Vi börjar skriva testet, skapar en i stort sett tom funktion så att vi kan kompilera projektet och köra testet.


//Testprojektet
        [TestMethod]
        public void CanParseEmptyStringToInteger()
        {
            ParseStringToInteger parser = new ParseStringToInteger();

            int actual = parser.ParseToInteger(String.Empty);

            Assert.AreEqual(0, actual);
        }

//Logik klassen
        public int ParseToInteger(string stringToParse)
        {
            return -1;
        }

När vi kör testet första gången får vi ett rött fallerande test precis som väntat.

Det absolut minsta vi behöver göra för att få koden att fungera är att ändra så att vi skicka tillbaks 0. Vi uppdaterar koden så att det står return 0;
 i funktionen istället och kör testet igen. Det blir grönt för att testet gick igenom. Kommer det här fungera senare? Det spelar ingen roll! För man koncentrerar sig på ett test i taget och det absolut lättaste sättet att få det att gå igenom var att göra såhär. Då är det rätt sätt.

Uppenbarligen har vi inte täckt in alla krav än så vi skapar ett test till där vi skickar in 1.

[TestMethod]
        public void CanParseStringWithNumberOneInToInteger()
        {
            ParseStringToInteger parser = new ParseStringToInteger();

            int actual = parser.ParseToInteger("1");

            Assert.AreEqual(1, actual);
        }


Testet misslyckas så våran nuvarande kod täcker inte in det nya testfallet än. Vi gör följande uppdatering:

public int ParseToInteger(string stringToParse)
        {
            return int.Parse(stringToParse);
        }


Nu går det andra testet igenom men det första misslyckas. Vi måste lägga till ett sätt för att se om strängen är tom och sen vet vi sen tidigare att vi bara behöver returnera 0 för att kravet ska uppfyllas för det första testet också.

public int ParseToInteger(string stringToParse)
        {
            if (string.IsNullOrEmpty(stringToParse))
            {
                return 0;
            }

            return int.Parse(stringToParse);
        }


Nu vet vi att funktionen gör precis det vi förväntar oss av den. Den parsar en sträng till en siffra och alla krav är uppfyllda och vi skapade först tester från kraven och sen funktioner som uppfyllde testerna.

Är det krav på att vi ska validera det som kommer in kan vi förstås lägga till funktioner för det och fortsätta på samma sätt som vi gjort hittills.

Så lätt är TDD, kan kännas lite konstigt i början man har man väl vant sig vill man aldrig sluta.



Vad krävs för att komma igång med TDD?

clock november 17, 2008 08:41 by author Magnus Härlin

Vad tjänar man på jobba med TDD jämfört med att skriva testen efteråt? I båda fallen skriver man test för samma funktioner och kan få samma code coverage.

 

TDD leder ofta till bättre design och mindre buggig mjukvara. Anledningen är att utvecklare inte motiverade att hitta felen i sin kod utan bara visa att den som fungerar.

 

Vad finns det för problem med att arbeta med TDD? Det är en ganska hög tröskel för att komma igång med det. Dom tre största inlärningsmomenten är:

·         Lära sig skriva bra enhetstest

·         Lära sig att låta testen driva utvecklingen (TDD)

·         Lära sig att göra en bra design

 

I den ordningen är det bra att lära sig momenten också.

 

Vad är det då som gör ett test bra? Framförallt är det att det är lättläst test där det är tydligt vad det testar. Det är absolut inte ett krav att ett test ska testa all funktionalitet i en klass utan hellre flera lättlästa korta test som tydligt testar olika delar av klassen.

 

I nästa blogginlägg ger jag exempel på hur man kan driva designen med test.

 

En best practice för att det ska vara bra design är att följa S.O.L.I.D

·         Single Responsibility Principle (SRP)

·         Open Closed Principle (OCP)

·         Liskow Substitiution Principle (LSP)

·         Interface Segragation Principle (ISP)

·         Dependency Inversion Principle (DIP)

 

För att hitta bra förklaring och översikt över principerna kan man titta in på: http://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablo-s-topic-of-the-month-march-solid-principles.aspx

 

Som med allt annat så är bästa sättet att lära sig samtliga delar att öva. Sen är det klart att det kan gå lite fortare och vara roligare om man har någon mer erfaren i teamet som man kan fråga och få guidning av.



Grön IT

clock november 13, 2008 20:58 by author Magnus Härlin

På den generella TechEd sessionen idag så var temat grön IT. Alla vet vi väl att datorer drar ganska mycket ström och ännu mer går det åt när det är hosting med redundans och kylning m.m.

Men visste du att 2% av USAs energiförbrukning går åt till att driva datorer? Det är mer ström än vad som används av samtliga tv apparater i USA...

Ett sätt att förbättra det på är att använda Cloud computing som börjar komma mer och mer nu. Ett annat kanske vanligare scenario är att datahallen kanske har en delat redundans. Troligen kommer inte alla kunders serverar kracha samtidigt och då räcker det i många fall med en delad redundant server dit den/dom virituella maskinerna på den krachande servern automatiskt kan flyttas vid problem.



Spela upp acceptanstest med Camano

clock november 13, 2008 13:24 by author Magnus Härlin

Imorse passade jag på att leka lite i hands on labs med Camano. Det är klart att det låter bra när någon står och pratar om inspelade test och möjlighet till att debugga i efterhand som jag skrev om igår.

 

Det går att lägga in acceptanstest (manuella test) i TFS som har steg för steg instruktioner hur man ska utföra testet och vad man ska använda för värden, precis som vilket vanliga acceptanstest som helst. Personligen tycker jag det är ganska tråkigt att sitta och utföra acceptanstest men får man tillbaks en bugg från ett så måste man ju fixa den, kolla att den går igenom och checka in ändringen. Nu har det gjorts lite lättare. När man har fixat buggen kan man med hjälp av Camano välja om man vill spela upp valda eller alla delar av testet igen. Det som händer då är att den replikerar dom stegen som testaren gjorde med att fylla i värden och sen så får man snabbt bara verifiera att slutresultatet är det förväntade.

 

Jag blir glad över alla manuella tester och steg jag kan slippa som utvecklare och det här är bara ännu ett steg i rätt riktning J



Barcelona TechEd 2008

clock november 12, 2008 19:21 by author Magnus Härlin

TechEd Barcelona 2008 är i full gång. Det är stort fokus på Visual Studio 2010 och .Net 4.0.

Med VisualStudio 2010 kommer testverktyget ”Camano”. Det kommer att ge möjligheten att spela in ett test där man klickar sig igenom en webb site och får allting inspelat. Så hittar en testare en bugg lägger man till den i TFS, attachar inspelningen och utvecklaren som tar hand om buggen kan exakt vad testaren gjorde och till och med debugga koden steg för steg. Det tycker jag låter helt otroligt bra!

Det kommer även att finnas AJAX support för UI testningen så att det inte är någon hemlighet vad det skickas för data fram och tillbaka.

Stöd för att testa Thread-testning kommer också att finnas med, kanske till och med någon slags visualisering av dom threads man har satt igång och informationen om dom. Det ger möjlighet att lättare upptäcka vad som skapar dead-locks till exempel. Testramverket för det här går under namnet ”Chess”. Finns redan ett ute på marknaden som inte MS har gjort som heter Race.

TFSen ska få betydligt bättre stöd för Continuous Integration och deployment. Det kommer att vara möjligt att göra en deploy där man har virituella maskiner som alltid är i ett bestämt state dit deployen görs. Det betyder att för varje deploy kommer dom att nollställas igen och det blir som en nyinstallation och på ett bekvämt sätt kan man få en bättre replika av sin produktionsmiljö.

Om det är en stor uppgradering på gång av ett system kan man alltså ha den gamla miljön uppsatt och köra alla uppgraderingsscript och den nya koden varje natt och ha ett kvitto varje morgon på att uppgraderingen fungerar även efter dom senaste kodändringarna.

Det här är bara en liten munsbit av allt intressant som har pågår här just nu. Jag återkommer senare med mer roliga nyheter.