TFS 15 RC2 – User Stories verschwunden

Standard

Das Update lief mit Hilfe des Wizards problemlos durch.
Am nächsten Tag waren aber keine User Stories mehr im Backlog. Auf die Schnelle haben wir den Grund nicht gefunden und das Backup eingespielt. -> Problem gelöst (erstmal).

Nun waren am Tag darauf die User Stories wieder weg. Die Suche begann also von Neuem.
Einen Hinweis auf die gelöschten User Stories haben wir in der Tabelle „WorkItemsDestroyed“ des Teamprojektes gefunden.
Am ChangeDate haben wir gesehen, dass irgendetwas die Löschung ausgeführt hat, denn um 22:35:39.000 war niemand mehr im Büro.

Der TFS führt viele Jobs automatisiert aus.
Für TFS 15 RC 2 habe ich bisher leider keine Aufstellung gefunden, allerdings zeigt ein Blick in die Datenbank alle Jobs (siehe unten)

Diese Jobs werden vom „Visual Studio Team Foundation Background Job Agent“ Service (TfsJobAgent.exe) ausgeführt.
Das Logging des TfsJobAgent zu erhöhen hat uns auch nicht weiter gebraucht, da gleich einige Jobs los liefen.
In der TfsJobAgent.exe.config kann man einfach das Vorbereitet Logging wieder ein kommentieren. Das hat Microsoft ganz gut gemacht.
Leider hat dies auch nichts gebracht, da es viele Jobs gibt, jedoch keiner schrieb, dass er was löscht.

In der Teamprojekt Datenbank sind die Jobs in der Tabelle tbl_JobSchedule zu finden, die Definition ist unter tbl_JobDefinition zu finden. Über die ScheduledTime haben wir die JobId gefunden und dann in der tbl_JobDefinition gesehen was der Job wirklich macht.
Der Job Name ist „Work Item Tracking Administration (Daily)“ und führt eine Prozedur aus „EXEC dbo.RemoveUnusedContent @partitionId = %%PARTITIONID%%“

In der Prozedur gibt es folgende Stelle:

 SELECT *
 FROM WorkItemTypes WIT
 INNER JOIN Constants C
 ON C.PartitionId=WIT.PartitionId
 AND WIT.fDeleted=1
 AND C.ConstId = WIT.NameConstantID
 INNER JOIN tbl_ClassificationNode N
 ON N.PartitionId=WIT.PartitionId
 AND WIT.ProjectId=N.Id
 INNER JOIN tbl_WorkItemCoreLatest WIC
 ON WIC.PartitionId=WIT.PartitionId
 AND WIC.WorkItemType = C.DisplayPart
 AND WIC.DataspaceId = N.DataspaceId

Wir hatten im Ursprungstemplate Scrum TFS 2013 den Typ Namen „Produkt Backlog Item“ auf „User Story“ geändert.
Durch das Update wurde jetzt unsere umbenannte User Story als gelöscht makiert fDeleted=1 und ein neuer WorkItemType „User Story“ eingefügt.

Durch den INNER JOIN auf DisplayPart hat die RemoveUnusedContent Prozedur unsere User Story mit dem fDeleted=1 gefunden und alle unsere Stories gelöscht.

Die Lösung war NameConstantID unsere angepasst User Story zu ändern und das fDeleted auf 0 zu setzen.

update WorkItemTypes set NameConstantID = id12354, fDeleted = 0 
where WorkItemTypeID in (id1, id2) and fDeleted = 1;

Danach hat die Prozedur unsere eigene Story gefunden, aber nicht mehr gelöscht.

Microsoft war sehr hilfsbereit und hat auf unseren Post schnell geantwortet und uns weiter per Mail unterstützt

Team Foundation Server “15” RC 2 available

Am besten also nie was am Template ändern oder auf den RTM warten 🙂

Tfs Widget

Standard

Microsoft bietet mit dem Update 3 RC ein SDK für die Entwicklung eigener Widgets an.
Die Anleitung ist ziemlich gut. Es werden Schritt für Schritt verschiedene Beispiel gezeigt, die man als Template für sein eigenes Widget heranziehen kann.

Grundprinzip ist eine HTML Seite mit einer json Datei zur Konfiguration -> das war es auch schon. Es ist kein Visual Projekt notwendig; ein Texteditor reicht 🙂
Um im eigenen Widget auf den TFS zuzugreifen bietet Microsoft eine Javascript Bibliothek an VSS.SDK.min.js, die in die HTML Seite eingebunden werden muss.

<script src="sdk/scripts/VSS.SDK.min.js">

In der json Datei wird im Abschnitt „contributions“ eine eindeutige ID für das Widget vergeben „“id“: „DoLiBlogShower“.
Diese ID muss im Javascript Abschnitt registriert werden „VSS.register(„DoLiBlogShower“, ……..
Erstellt wird das Widget einfach per Kommandozeile mit „tfx extension create“. Evtl. sind dazu per npm noch ein paar Pakete zu installieren

Hinweis zur Widget Size:
Leider geht derzeit die Verlinkte Seite zur WidgetSize nicht. Festgestellt habe ich, dass eine maximale Größe von 4 x 4 möglich ist. Sobald mehr angegeben ist, wird das Widget nicht mehr in der Auswahlliste angezeigt. Die Anzeige auf dem Dashboard funktioniert nach einem Update dann auch nicht mehr.

"supportedSizes": [
    {
        "rowSpan": 4,
        "columnSpan": 4
    },
    ...

Zum Test hab ich einfach meinen Blog im Iframe in den TFS gepackt. Den Beispielcode findet ihr wie immer auf GitHub  https://github.com/DominikLindemann/BlogExamples.git

Anzeige im TFS ist dann wie folgt:

in der Widget Auswahl

in der Widget Auswahl

im Dashboard

im Dashboard

unter Extension Verwaltrn

unter Extension Verwalten

unter Manage Extensions

unter Manage Extensions

Edge Http 500 wenn Fehlermeldung kleiner 512 Byte

Standard

Natürlich Verhalten sich alle Browser unterschiedlich und auch der Microsoft Edge macht da keine Ausnahme.
So unterdrückt der Edge Fehlermeldungen, wenn diese kleiner als 512 Byte sind und zeigt HTTP 500 an.
edge error 500

Ein Ticket bei Microsoft gibt es dazu bereits

Uns ist dies aufgefallen als wie einen Soap Webservice geschrieben haben und die Fehlermeldung nicht angezeigt wurde.
Lösung ist jetzt die Exception auf die Größe zu prüfen und im Fall der Fälle ein paar Leerzeichen anzuhängen.

Beispiel findet ihr unter  https://github.com/DominikLindemann/BlogExamples.git

CodedUI Tests mit Selenium und VNext Build

Standard

Schon länger überlegen wir, unsere Webseite auch über Tests prüfen zu lassen. Die API wird durch verschiedene Tests (Unittest, Acceptanztest und Systemtests) bereits geprüft, wieso also nicht auch die UI automatisiert testen.
Natürlich kommen an dieser Stelle die üblich Fragen:
– Wie viel darf es kosten?
– Was wollen wir eigentlich testen?
– Welche Browser / Browserversionen sollen getestet werden?

Wir haben relativ schnell gemerkt, dass CodedUI Tests aufnehmen und abspielen nicht der beste Weg ist, da Wartung und Erweiterung des Tests fast nicht möglich sind.
Die Lösung war, ein  Metadaten Model über unsere Webseite zu legen. So konnten wir lesbaren und erweiterbaren Code schreiben. Nun liefen unsere Tests aber leider nur im IE und auch leider nicht in einem automatischen Build.
Für die Build-Automatisierung haben wir uns für das Tool Selenium entschieden.

Hier die Vorgehensweise:
– Neue Solution aufsetzen
– Selenium Nuget Pakete installieren  (es werden die Driver.exe(n) der Solution hinzugefügt. Diese sollten nicht eingecheckt werden. Wir haben noch eingestellt, dass sie jedesmal ins „Output Dir“ kopiert werden)

selenium installation

 

So könnte der QuellCode eines Tests aussehen:

[TestMethod]
public void DummyTest()
{
    IWebDriverdriver=null;
    try
    {
        driver=GetFireFoxDriver();
        driver.Navigate().GoToUrl("http://myWebsite/ui/dist/index.html");
 
        IWebElementuserInput=driver.FindElement(By.Id("userId"));
        IWebElementpwdInput=driver.FindElement(By.Id("pwdId"));
        IWebElementbutton=driver.FindElement(By.Id("loginBtnId"));
 
        userInput.SendKeys("userlogin");
        pwdInput.Clear();//InputBox löschen um sicherzugehen,dass der Browser sich nichts gemerkt hat.Wichtig bei Edge
        pwdInput.SendKeys("userpassword");
        button.Click();
 
        var waiter=newWebDriverWait(driver,TimeSpan.FromMinutes(5));//warten bis das Controll erscheint.Maximal 5 min
        var logoutButton=waiter.Until(x=gt;x.FindElement(By.Id("logoutButtonId")));
 
        logoutButton.Click();
    }
    finally
    {
        driver?.Close();
    }
}

private IWebDriverGetFireFoxDriver()
{
    var proxy=GetProxy(); 
    var firefoxProfile = newFirefoxProfile(); 
    firefoxProfile.SetProxyPreferences(proxy); 
    firefoxProfile.DeleteAfterUse=true; 

    string serverPath=@"MozillaFirefox\firefox.exe"; 
    if(System.Environment.Is64BitOperatingSystem) 
    { 
        serverPath=Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"),serverPath); 
    } 
    else 
    {
        serverPath=Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"),serverPath); 
    } 

    return newFirefoxDriver(new FirefoxBinary(serverPath),firefoxProfile,TimeSpan.FromMinutes(2)); 
}

private static ProxyGetProxy() 
{ 
    return newProxy
    {
        proxy.NoProxy="*.meinDomain.de,localhost"; 
        proxy.Kind=ProxyKind.Manual; 
        proxy.IsAutoDetect=false; 
        proxy.HttpProxy="proxyserver:1234"
    };
}

Nachdem dies nun geht, wollen wir natürlich auch noch automatisierte Builds, die auf verschiedenen Clients auf den unterschiedlichsten Browser Versionen installiert sind.

Einstellungen im TFS:
Zuerst sollten wir eine Computergruppe einrichten. Dazu den Tab „Test“ anklicken und danach das Menü „Computer“.
Als Name wählen wir z.B. UITests und füllen die weiteren Felder aus. Diese Gruppe wählen wir dann später in unserem Build aus.
Muss in 3 Buildschritten eingetragen werden: Windows Maschine File Copy (Machines), Visual Studio Test Agent Deployment (Test Machine Group) und Run Functional Tests (Test Machine Group)
computergruppe

 

 

 

 

 

 

Danach kopieren wir noch den TestAgent von Microsoft auf den Testclient und lassen ihn installieren, falls der TestAgent noch nicht installiert ist.

Wir legen einen neuen Build an.  Im Buildschritt „Visual Studio Test Agent Deployment“ stellen wir bei Test Machine Group die vorher erstellte Maschine ein (UITests):

agent deployment
Hinweis: Die Installation des TestAgents benötigt ca 2 GB Speicherplatz. 

 

 

 

 

 

 

Um die Tests auf dem TestClient laufen zu lassen, müssen die Dateien dort auch hinkopiert  werden. Hier „c:\ToTest“ (Freigabe auf dem TestClient)
run tests

 

 

 

 

Damit ist die Konfiguration des Builds abgeschlossen.

Auf dem Testclient müssen noch ein paar Einstellungen vorgenommen werden:

Einstellungen Testclient:
1.) Build User in die Gruppe Admin
2.) .Net Framework installieren -> hier 4.6, ob es mit älteren Versionen funktioniert habe ich nicht ausprobiert
3.) Verzeichnis für DropLoaction anlegen und freigeben für den Benutzer, der den Test ausführt.
4.) Remote UAC deaktivieren -> regedit ->  HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy
„Set the value data to 0 to disable remote UAC processing and to 1 to enable it.“
5.) Nun Powershell als Admin starten und folgendes ausführen:
–  Enable-PSRemoting -force
– winrm quickconfig
– Set-Item WSMan:\localhost\Client\TrustedHosts *
6.) WMI Berechtigung anpassen:
WMI öffnen (Computerverwaltung -> Dienste und Anwendungen -> WMI Steuerung -> Eigenschaften)
– Eigenschaften -> Tab wechseln zu ‚Sicherheit‘ -> Button Sicherheit anklicken
– Build User eintragen und alle Rechte zulassen (keine Ahnung was genau gebraucht wird. Da der Rechner nur intern ist sicherheitshalber alles erlauben 🙂 )

Nun ist alles eingerichtet und die CodedUI Tests laufen auf dem TestClient.

Methoden Überladen (Lösung)

Standard

Auflösung:

Es wird „Derived.Foo(object)“ ausgegeben.
Warum ist dies so?

Der C# Compiler wählt die neu implementierte Methode Foo(object) anstelle der überschriebenen Methode Foo(int), unter der Massgabe das die übergebenen Parameter kompatibel sind. Dies liegt daran, dass überschriebene Methoden nicht als Klassendeklaration betrachtet werden, sondern als neue Implementierungen einer Methode, welche in einer Basisklasse deklariert ist. Kurz gesagt, durchsucht der Compiler erst die direkt deklarierten Methoden von Derived, dann die Methoden von Base die in Derived überschrieben sind und dann die Methoden von Base.

Das heißt, dass folgender Code zum gleichen Ergebnis führt:

namespace ConsoleApplication1
{
    class Base
    {
        public virtual void Foo(int x)
        {
            Console.WriteLine("Base.Foo(int)");
        }
    }

    class Derived : Base
    {
        public void Foo(object o)
        {
            Console.WriteLine("Derived.Foo(object)");
        }
    }

    class Program
    {
        static void Main()
        {
            Derived d = new Derived();
            int i = 10;
            d.Foo(i);
        }
    }
}

Wie kann man das vermeiden?

1. Da die Gleichbenamung eh ein suboptimaler Stil ist, sollte man einen anderen Namen vergeben!

2. Per Code und explizitem Casting gehts natürlich auch. Hier wird die Methodenliste von Base durchsucht um eine kompatible Methode zu finden. Da Foo virtuell ist, wird die Implementierung von Foo(int x) in Derived aufgerufen.

namespace ConsoleApplication1
{
    class Base
    {
        public virtual void Foo(int x)
        {
            Console.WriteLine("Base.Foo(int)");
        }
    }

    class Derived : Base
    {
        public override void Foo(int x)
        {
            Console.WriteLine("Derived.Foo(int)");
        }
    

        public void Foo(object o)
        {
            Console.WriteLine("Derived.Foo(object)");
        }
    }

    class Program
    {
        static void Main()
        {
            Derived d = new Derived();
            int i = 10;
            ((Base)d).Foo(i);
        }
    }
}

Es gibt noch eine weitere Möglichkeit, die ich zwar nicht wirklich unterstütze, aber der Vollständigkeit halber erwähne. Man kann die Implementierung der Basisklasse verbergen und die Methode als Deklaration der Derived Klasse bauen. Hierfür gibt es das Schlüsselwort „new“.

namespace ConsoleApplication1
{
    class Base
    {
        public virtual void Foo(int x)
        {
            Console.WriteLine("Base.Foo(int)");
        }
    }

    class Derived : Base
    {
        public new void Foo(int o)
        {
            Console.WriteLine("New Derived.Foo(int)");
        }

        public void Foo(object o)
        {
            Console.WriteLine("Derived.Foo(object)");
        }
    }

    class Program
    {
        static void Main()
        {
            Derived d = new Derived();
            int i = 10;
            d.Foo(i);
        }
    }
}

Gemäß dem oben beschriebenen Mechanismus der Methodenauswahl wird hier eine geeignete Methode innerhalb von Derived gesucht und mit Foo(int) auch gefunden, da diese Teil der Klassendeklaration ist.

Methoden überladen

Standard

Was wird ausgegeben und warum?

namespace ConsoleApplication1
{
    class Base
    {
        public virtual void Foo(int x)
        {
            Console.WriteLine("Base.Foo(int)");
        }
    }

    class Derived : Base
    {
        public override void Foo(int x)
        {
            Console.WriteLine("Derived.Foo(int)");
        }

        public void Foo(object o)
        {
            Console.WriteLine("Derived.Foo(object)");
        }
    }

    class Program
    {
        static void Main()
        {
            Derived d = new Derived();
            int i = 10;
            d.Foo(i);
        }
    }
}

VNext mit SonarQube

Standard

Um die Qualität unseres Codes (.Net C#) weiter zu verbessern haben wir eine Testinstallation von SonarQube vorgenommen. Ziel ist es zuerst mal aus dem VNext Build den Code in Sonar analysieren  zu lassen und darzustellen. Ob wir später SonarQube als Quality Gate benutzen wird sich noch zeigen.

Die Installation von SonarQube ist recht einfach und auch sehr gut beschrieben. Wer nicht so viel lesen will kann sich den Quickstart Guide anschauen.
Unser Installationsumgebung ist Win Server 2012 R2 mit SQL Server 2014. Die Installation lief wie folgt ab:
Sql Server :

  1. Datenbank (sonar) anlegen
  2. Benutzer für Datenbank anlegen und entsprechend mit Rechten verstehen
  3. Benutzer in die Konfigdatei eintragen

SonarQube Server einrichten:

  1. Zip nach c:sonar entpakt
  2. Scanner für MSBuild hinzufügen
  3. C# Plugin hinzufügen -> Runterladen und unter C:sonarextensionsplugins legen.
  4. Starten oder als Service installieren (C:sonarbinwindows-x86-64)
    – Starten „StartSonar.bat“ oder
    – Service installieren (InstallNTService.bat) -> Service User noch ggf. anpassen

Vnext Build anpassen: Detaillierte Anleitung ist auf CodeWrecks zu finden

  1. Buildschritt „SonarQube for MSBuild – Begin Analysis“ direkt als ersten Schriit hinzufügen
    – Project Key und Project Name eingeben -> werden automatisch in Sonar angelegt
    – Wer eine History und Vergleichsmöglichkeit in Sonar haben möchte muss die Project Version  dynamisch setzen. z.B. 1.$(Build.BuildNumber)
    – Die anderen Felder können leer gelassen werden
  2. Buildschritt „SonarQube for MSBuild – End Analysis“ am Ende hinzufügen

build mit sonar

Der Build Agent muss auch noch die entsprechenden Anforderungen erfüllen Ein Blick in die Builddefinition -> Allgemein -> Anforderungen genügt.
Bei uns fehlte Java.
Falls nicht alle Anforderungen erfüllt sind kommt eine aussagekräftige Fehlermeldung.

sonar agent requirements

TFS2015 Vnext: Neuen Buildschritt einspielen

Standard

In unseren bestehend XAML Builds werden Nuget Pakete erstellt und auch auf gepublished. Dazu hatten wir das Default XAML Template entsprechend angepasst.

Nun steht die Migration auf VNext Builds an.
Sollte in der Theorie ganz einfach sein. Buildschritt(e) einfügen und fertig. Aber so einfach ist es leider nicht. Unsere Version des TFS bietet diese Buildschritte nicht an.
Also doch wieder selber schreiben oder hat dies schon jemand? Nach etwas Recherche haben wir auf blogs.infosupport.com was gefunden. Super Anleitung aber gibt’s da nicht was von Microsoft?

Und tatsächlich vor 6 Tagen sind auf GitHub die Tasks aufgetaucht.
packAndPublishStep

Also Download/Clone und dann die Tasks hochgeladen und ins Build aufgenommen.
Das Skript zum Upload aus dem Infosupport Blog stelle ich auf meinem GitHub zur Verfügung.
Der Aufruf wäre z.B. & ‚.upload build step.ps1‘ ‚C:demovso-agent-tasks-masterTasksNugetPackager‘ ‚http://myTfs:8080/tfs‘

Dabei werden dann noch die Login Daten abgefragt -> eingeben -> fertig
tfs login

Basta 2015 – So war mein erster Besuch

Standard

Dieses Jahr war ich das erste Mal auf der Basta und wurde von den vielen Neuerungen erschlagen.
Dazu später mehr.

Zuerst das Allgemeine:
Die Organisation war hervorragend und falls man doch Fragen hatte fand man schnell einen hilfsbereiten Mitarbeiter.
Toll war die App zur Basta! Einfach und schnell konnte ich so meine Session-Besuche planen und hatte immer und überall einen Überblick was es wie und wo gab.
Für Verpflegung war auch gesorgt und es lieben keine Wünsche offen. Kaffee war auch genug da

Ich durfte von Montag bis Donnerstag Workshop und Sessions besuchen. Obwohl die Tage sehr lange waren verging die Zeit wie im Flug.

Super fand ich die Night School mit Dariusz Parys „Du willst es doch auch! Spieleprogrammierung mit HTML5 und JavaScript“ -> ja ich will es auch !!!!

Mein Fazit:
Die Schlagwörter für mich waren dieses Jahr Cross Platform und Cross Device.
Microsoft bietet mit dem Fullframework 4.6 jede Menge dafür und wird dies in .Net Core noch deutlich erweitern und verbessern.
Mit den „Tools“ Cordova und Xamarin“ bekommen sowohl  C# Entwickler als auch HTML und Javascript Entwickler relativ einfach die Möglichkeit Cross Platform Apps zu entwickeln.
Diese Möglichkeiten machen es jetzt nicht einfacher die richtige Technologie für sein Projekt zu wählen.

Windows 10 war natürlich auch ein wichtiges Thema. Wurde auch direkt im Eröffnungspanel diskutiert

Spannend bleibt auch abzuwarten wie es Microsoft gelingt bei .Net Core die Nuget Pakete zu verwalten. Wird es irgendwann einen Paketmanager in jedem Entwickler Team geben?

Freu mich schon auf die nächste Basta
Bis dahin viel Spaß beim Ausprobieren der neuen tollen Dinge