/Images/MainImage/Håkan Alexander.jpg finns inte.

2009-09-02

Outsource och IT-support

På mina uppdrag i stora organisationer som har en väl utvecklad och outsourcead IT-support och helpdesk har jag funderat lite över ett fenomen. Supporten har ingen vilja eller incitament att vara hjälpsamma och på riktigt vilja lösa problemen. Jag inser att jag generaliserar och att avvikelser såklart finns men jag ser ett mönster.

Såklart tjänar man på att ha goda rutiner och standardiserade förfaranden för hur applikation, rättigheter och dylikt installeras. Men leverantörerna som är upphandlade att stödja organisationen i hanteringen av datorer och programvara är det i konkurrens och ett av dom billigaste anbuden är den troliga vinnaren. Leverantören vill naturligtvis utföra uppdraget med en rimlig vinst, han är troligen inte så motiverad att utveckla sina system och göra dem mer användarvänliga.

Som jag har uppfattat det i mitt användande av dessa tjänster är att dom på supporten är mer intresserade av att stänga ärendet än att verkligen lösa problemet. Har man skrivit i något felaktigt i förfrågan så stängs ärendet och man ombeds göra en ny. Och de verktygen/systemen där man som användare förväntas göra sin anmälan lämna mycket övrigt att önska. Ett sådant var den värsta användarupplevelsen jag någonsin sett! Och jag har sett ett och annat. Dessa är utformade på ett sätt så att det passar den som skall utföra uppgiften och är därmed nästan oanvändbara för den lekman som behöver hjälpen. På ett ställe där jag behövde rättigheter i en utvecklingsmiljö tog det mig med hjälp av 4 andra som var väl insatta i det system jag behövde rättigheter flera timmar att hitta fram till rätt nod i det träd av applikationer/rättigheter och annat. Och då hade jag först gjort ett felaktigt försök som genererat ett stängt ärende. Jag är ändå systemutvecklare och datoranvändare sedan 25 år så många av termerna är jag ändå van vid, trots det så är det svårt att förstå.

Bra statistik för både leverantör och kund! IT-supporten hanterar många ärenden och dom gör det effektivt. Och kunden har fått mycket för sina pengar, billigt per ärende.

Men dessa dolda kostnader som inte syns? Användare sitter och väntar och kan inte komma vidare, ibland i flera dagar. Hjälp av andra behöver man ta så att det stör deras arbete också.

Jag undrar om det kommer en våg av managementkonsulter och börjar mäta denna typ av kostnader inom ett par år?


Postad av Håkan Alexander

Kommentarer (0) 



2009-05-14

Varje system skall äga sitt eget data

En mycket intressant och läsvärd post om problemen med att dela data mellan olika applikationer.

Commercial Suicide - Integration at the Database Level av Jak Charlton

Rekommenderad läsning!


Postad av Håkan Alexander

Kommentarer (0)    Kategorier:  Läsvärt



2009-04-23

Filmtips

Om du bara skall se en film i helgen se denna! Nej då jag skojar.

Men nu har äntligen inspelningarna från ALT.NET spåret på Öredev 2008 blivit klara.

Dom finns här http://www.viddler.com/explore/oredev/

Jag kan speciellt rekomendera Ayendes session om Active Record. Den var mycket bra, tänkvärd och underhållande.

Se den!

/Håkan 


Postad av Håkan Alexander

Kommentarer (0)    Kategorier:  Oredev2008    NHibernate    Tools



2009-02-22

Källkod till bloggen

När man vill visa källkod i sin blog finns det en fin liten plugin till VisualStudio som fixar det hela .

http://www.jtleigh.com/people/colin/software/CopySourceAsHtml


Postad av Håkan Alexander

Kommentarer (0)    Kategorier:  Tools



2009-02-20

BDD med NUnit

"BDD is TDD done right" har någon sagt och jag är benägen att stämma in i den kören. Jag har inte jättelång erfarenhet av testdriven utveckling men 2 saker har hjälpt mig mycket i att minska underhållet av testerna när man lägger till funktionalitet till metoder som redan har tester.

1. Att bara ha ett logiskt test i varje testmetod. Tex att bara ha ett mockat object och låt de andra objekten som skall interagera vara stubbar som bara hjälper till att sätt upp förutsättningarna för att kunna testa interaktionen med det mockade objektet. Visst kan man ha flera asserts i en testmetod men de skall testa samma sak, man kanske kollar att metoden inte returnerar null och sedan att den returnerar rätt värde/objekt men 2 asserts men det är bara för att underlätta när det går fel genom att felmeddelandet blir tydligare.

2. Att med hjälp av klassnamn och metodnamn för testerna utförlig beskriva vad som testas, i c# kan både klassnamn och metodnamn vara 512 tecken långa så det kan bli en ordentlig menig. Detta gör man för att så detaljerat som möjligt beskriva det beteende som testas så att den som senare får ett felande test med hjälp av den texten kan förstå vad som testas utan att behöva analysera varken testkod eller kod under test. För i ärlighetens namn så är det inte roligt att läsa någon annans kod eller sin egen gamla heller för den delen.

Om man med hjälp av testklasser och testmetoder kan beskriva vad som testas så får man ett bra underlag för vad system kan och gör. Med lite xslt transformering kan testresultatet bli en riktigt läsbar upplevelse och alltid aktuell dokumentation.

Här finns ett enkelt exempel. 

 

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
  <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:template match="test-results">
    <html>
      <head>
        <style>
          <![CDATA[
            body { font : 1em verdana; }
            li { list-style-type : none; padding-top:0.5em; }
            li li { padding-top:0; font-size : .9em; }
            li.failed { color:red; }
          ]]>
        </style>
      </head>
      <body>
        <ul>
          <xsl:apply-templates select="//test-suite"/>
        </ul>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="test-suite">
    <xsl:if test="not(contains(@name, '\'))">
      <xsl:variable name="class-name" select="@name" />
      <li>
        <xsl:variable name="context">
          <xsl:call-template name="remove-under-score">
            <xsl:with-param name="text" select="$class-name" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$context"/>
        <ul>
          <xsl:for-each select=".//test-case">
            <xsl:variable name="test-name">
              <xsl:call-template name="test-case">
                <xsl:with-param name="class-name" select="$class-name"/>
                <xsl:with-param name="full-method-name" select="@name"/>
              </xsl:call-template>
            </xsl:variable>
            <xsl:element name="li">
              <xsl:if test="@success = 'False'">
                <xsl:attribute name="class">
                  <xsl:value-of select="'failed'"/>
                </xsl:attribute>
              </xsl:if>
              <xsl:value-of select="$test-name"/>
            </xsl:element>
          </xsl:for-each>
        </ul>
      </li>
    </xsl:if>
  </xsl:template>
 
  <xsl:template name="test-case">
    <xsl:param name="class-name"/>
    <xsl:param name="full-method-name"/>
    <xsl:variable name="method-name">
      <xsl:call-template name="remove-namespace">
        <xsl:with-param name="class-name" select="$class-name"/>
        <xsl:with-param name="full-method-name" select="$full-method-name"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="method-sentence">
      <xsl:call-template name="remove-under-score">
            <xsl:with-param name="text" select="$method-name"/>
      </xsl:call-template>
    </xsl:variable>
 
    <xsl:text>- </xsl:text>
    <xsl:value-of select="$method-sentence"/>
  </xsl:template>
 
  <xsl:template name="remove-under-score">
    <xsl:param name="text"/>
    <xsl:value-of select="translate($text, '_', ' ')"/>
  </xsl:template>
 
  <xsl:template name="remove-namespace">
    <xsl:param name="class-name"/>
    <xsl:param name="full-method-name"/>
    <xsl:variable name="namespace-end" select="concat($class-name, '.')"/>
    <xsl:value-of select="substring-after($full-method-name, $namespace-end)"/>
  </xsl:template>
 
  <xsl:template name="remove-keywords">
    <xsl:param name="text" />
    <xsl:variable name="removed-Behavior">
      <xsl:call-template name="string-replace-all">
        <xsl:with-param name="text" select="string($text)"/>
        <xsl:with-param name="replace" select="string('Behavior')"/>
        <xsl:with-param name="by" select="string('')"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="removed-Tests">
      <xsl:call-template name="string-replace-all">
        <xsl:with-param name="text" select="string($removed-Behavior)"/>
        <xsl:with-param name="replace" select="string('Tests')"/>
        <xsl:with-param name="by" select="string('')"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$removed-Tests"/>
  </xsl:template>
 
  <xsl:template name="string-replace-all">
    <xsl:param name="text"/>
    <xsl:param name="replace"/>
    <xsl:param name="by"/>
    <xsl:choose>
      <xsl:when test="contains($text, $replace)">
        <xsl:value-of select="substring-before($text, $replace)"/>
        <xsl:value-of select="$by"/>
        <xsl:call-template name="string-replace-all">
          <xsl:with-param name="text" select="substring-after($text, $replace)"/>
          <xsl:with-param name="replace" select="$replace"/>
          <xsl:with-param name="by" select="$by"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

 


Postad av Håkan Alexander

Kommentarer (1)    Kategorier:  TDD



2009-02-18

Gränssnittsprototyper

När du vill göra gränssnittsprototyper att diskutera runt kan jag verkligen rekomendera Balsamiq Mockups.

På ett kick skapar man prototyper som fungerar riktigt bra att diskutera utifrån. Dom är tillräckligt prydliga för att  ge ett bra intryck men samtidigt inte så detaljerade att det börjar diskuteras färgdetaljer och liknande.

Exempel från www.balsamiq.com 

För mig som inte har någon större känsla för färg och form, och dessutom tecknar illa, funkar Balsamiq utmärkt.


Postad av Håkan Alexander

Kommentarer (0)    Kategorier:  Tools



2009-01-27

Validering och entiteter

Jag har funderat en del runt hur man skall validera inmatat data på ett smidigt sätt i ASP.NET MVC. Det finns massor med bloggar att läsa i ämnet samt ett och annat ramverk som kan generera klientvalidering med javascript. Dessa ramverk förutsätter att man använder attribut på entiteternas egenskaper för att beskriva vilka regler som gäller för den specifika egenskapen.

Men vad är det man vill åstadkomma med reglerna för en egenskap? Som jag ser det så vill man skydda sin domän mot att felaktig data kommer in i systemet. Då behöver man antingen använda aspekter för att validera tilldelningen av data eller skriva valideringen i tilldelningskoden vilket ger duplicering. Som jag ser det är det bästa sättet att skydda domänen mot felaktigt data är att använda Value Objects i stället för Value Types. Tex: Ett UserName objekt i stället för attributen [Required][MinLength(8)][MaxLength(20)] på en egenskap av typen String. Mer om Value Objects här.

I mitt tycke är det denna väg man vill gå för att så tidigt som möjligt detektera felaktigt data samt att skydda domänen mot det. Återstår "bara" att få det att fungera smidigt i ASP.NET MVC controllerna.


Postad av Håkan Alexander

Kommentarer (2)    Kategorier:  ASP.NET MVC    DDD



2008-12-10

Integrationstest mot databas

Jag fick en kommentar från Karl på min blogg om Dataaccess och kundnytta där han undrade om hur jag gjorde integrationstester mot databasen så jag skriver om det.

Det viktigaste är att man har känt data i databasen när man skall göra dessa integrationstester.

För att inte störa eller störas av andra som jobbar mot databasen behöver man en egen databas jag vill helst köra lokalt för att slippa att testerna tar onödigt lång tid.

Jag har använt mig av NDbUnit  för att säkerställa att jag har känt data i databasen innan testerna körs. Det är ett litet program som kan fixa till data på massor av sätt, jag har valt att låta den tömma databasen och fylla på med mitt testdata före integrationstesterna körs.

I mitt senaste projekt hade vi en DBA med som jobbade mot en gemensam databas, för att säkerställa att vi var i synk gjorde jag så att testkoden kan gå mot olika databaser beroende på behov.

Nedan finns källkoden som körs före något test i det namespace koden ligger. Alltså den kod som puttar in det data i databasen jag vill testa mot.

Sen kan jag enkelt skriva tester som verifierar tex att mina mappningar är korrekt om jag kör NHibernate eller att min dataaccess kod sätter alla properties.

    1 using System;

    2 

    3 using NUnit.Framework;

    4 

    5 using DBAccess;

    6 

    7 namespace DBAccessTest

    8 {

    9     [TestFixture]

   10     public class OrderTests

   11     {

   12         [Test]

   13         public void CanGetOrderById()

   14         {

   15             var dao = new OrderDao();

   16 

   17             var order = dao.GetById(1);

   18 

   19             Assert.That(order, Is.Not.Null);

   20             Assert.That(order, Has.Property("Created").EqualTo(DateTime.Parse("2008-12-10 13:34")));

   21         }

   22 

   23         [Test]

   24         public void OrderHasOrderRows()

   25         {

   26             var dao = new OrderDao();

   27 

   28             var order = dao.GetById(2);

   29 

   30             Assert.That(order.OrderRows, Has.Count.EqualTo(3));

   31         }

   32     }

   33 }

OBS. Testar NUnit 2.5 därav syntax som inte finns i 2.4!

Det är lika lätt att skriva tester för insert, update och delete. Dessa kan man köra i en transaktion runt själva testet och rulla tillbaka för att det inte skall påverka följande tester.

 

    1 using System.Data;

    2 

    3 using NUnit.Framework;

    4 

    5 using NDbUnit.Core;

    6 using NDbUnit.Core.SqlClient;

    7 

    8 namespace DBAccessTest

    9 {

   10     // Supported enviroments

   11     public enum TestDatabases

   12     {

   13         local,

   14         remote

   15     }

   16 

   17     // Setup for tests in the same namespace

   18     [SetUpFixture]

   19     public class SetupDatabaseWithKnownData

   20     {

   21         // Change this when you would like to verify other database

   22         private TestDatabases currentDatabase = TestDatabases.local;

   23 

   24         // Filename of data stored during test

   25         private string original_data_file = "org_data.xml";

   26 

   27         // Connections to different databases

   28         private string local_connection = @"Data Source=.\SQLEXPRESS;Initial Catalog=testdatabase;Integrated Security=True";

   29         private string remote_connection = @"Data Source=192.168.1.120;Initial Catalog=testdatabase;Integrated Security=True";

   30 

   31         // Return current connection

   32         private string CurrentConnectionString()

   33         {

   34             string connectionString = "";

   35 

   36             switch (currentDatabase)

   37             {

   38                 case TestDatabases.local:

   39                     connectionString = local_connection;

   40                     break;

   41 

   42                 case TestDatabases.remote :

   43                     connectionString = remote_connection;

   44                     break;

   45             }

   46 

   47             return connectionString;

   48         }

   49 

   50         // Runs once prior to any test in this namespace

   51         [SetUp]

   52         public void SetupDatabase()

   53         {

   54             // Creating a new databaseSetup object with the relevant connectionstring

   55             INDbUnitTest databaseSetup = new SqlDbUnitTest(CurrentConnectionString());

   56 

   57             // Load the database schema, tables not in this schema will be ignored.

   58             databaseSetup.ReadXmlSchema("database_schema.xsd");

   59 

   60             // Save current data if running test against other source than my private

   61             if (currentDatabase != TestDatabases.local)

   62             {

   63                 DataSet original = databaseSetup.GetDataSetFromDb();

   64                 original.WriteXml(original_data_file);

   65             }

   66 

   67             // Load my known data into the databaseSetup

   68             databaseSetup.ReadXml("current_test_data.xml");

   69 

   70             // Insert that data into the database, clear all other data.

   71             databaseSetup.PerformDbOperation(DbOperationFlag.CleanInsertIdentity);

   72         }

   73 

   74         // Runs after all tests been executed

   75         [TearDown]

   76         public void RestoreDatabase()

   77         {

   78             // If not my database resore to original data.

   79             if (currentDatabase != TestDatabases.local)

   80             {

   81                 INDbUnitTest databaseSetup = new SqlDbUnitTest(CurrentConnectionString());

   82 

   83                 databaseSetup.ReadXmlSchema("database_schema.xsd");

   84 

   85                 // Load the data we stored earlier

   86                 databaseSetup.ReadXml(original_data_file);

   87 

   88                 // Clear and resore data from prior to tests

   89                 databaseSetup.PerformDbOperation(DbOperationFlag.CleanInsertIdentity);

   90             }

   91         }

   92     }

   93 }

Det finns en bugg i NDbUnit 1.2 som gör att CleanInsertIdentity inte fungerar om man har ForeignKey relations i sin databas. Det finns bloggar om hur man löser det.

 


Postad av Håkan Alexander

Kommentarer (2)    Kategorier:  TDD    NHibernate



2008-12-08

webcontrol i ASP.NET MVC

Jag skrev tidigare om att jag tyckte det kan bli grötigt i de views man skapar när man använder ASP.NET MVC och att jag efterlyste något liknande webcontrols. Läste nyss på Phil Haack's blog om en view engine som heter Spark det verkar vara det jag efterlyste, MÅSTE PROVA.


Postad av Håkan Alexander

Kommentarer (0)    Kategorier:  ASP.NET MVC



2008-12-06

Systeminformation på startsidan

Vi visste inte riktigt vad vi skulle använda startsidan till på den senaste webbapplikationen jag utvecklade. Så jag förslog för kunden att vi skulle göra något bloggliknande där systemintressenterna skulle kunna skriva meddelanden till användarna. Tex om stundande systemunderhåll, uppdateringsinformation eller information som verksamheten vill ha ut till användarna.

Som vanligt tänkte jag på en till tabell i databasen och en WYSIWYG-editor.

Men häromdagen slog det mig att det vore smartare att bara implementera en RSS-lista på startsidan och lägga en URL i web.config. Då kan man ju enkelt ha olika källor för olika miljöer, utvecklingsmiljön, UAT och produktion. Dessutom är den källan oberoende av databasen vilken i både utveckling och UAT rensas mellan varven. Med denna implementation skulle man enkelt kunna få tex incheckningshistoriken från i detta fallet TFS på startsidan i utveckling, UAT miljön skulle kunna innehålla information om vilka scenarios och buggar som är implementerade som genererats som statisk xml-fil i samband med att det installationspaketet byggs av byggservern och produktion skulle kunna visa det jag beskrev tidigare. För produktion skulle man kunna använda befintligt intranät för att skapa nya inlägg.

Känns som en ganska simpel men flexibel och användbar lösning.

Har inte hunnit kolla om TFS-08 har RSS-flöde på källkodshistoriken. Men hittade denna implementation som borde gå att använda. Och att skriva en RSS-lista tar ju inte många minuter.

Uppdaterat länken!


Postad av Håkan Alexander

Kommentarer (1)    Kategorier:  TFS