Rahmenbedingungen etablieren mit .NET CLI Vorlagen

Es ist wichtig für ein Team, sich nicht nur auf einen Spieler zu verlassen.

Luis Suarez

In einem unserer vergangenen Projekte haben wir für Rahmenbedingungen im Bereich der Testautomatisierung mit Vorlagen gearbeitet, die über NuGet in einer Projektmappe installiert werden konnten. Das Team verfolgte mit diesem Vorgehen die Reduzierung des Initialisierungsaufwands und die Etablierung von Rahmenbedingungen zur besseren Zusammenarbeit.

Der bisherige Ansatz

Die Vorlage stellte das Grundsetup für das Testen von Webbrowser-Anwendungen mit Browserstack, Selenium, SpecFlow und SpecRun bereit. Neben den NuGet-Paketen ist auch die Infrastruktur-Konfiguration für die Kommunikation mit Browserstack und lokalen Browsern wie Chrome oder Firefox sowie das Reporting in dieser Vorlage vorhanden. Setup-Dateien wurden mithilfe von NuGet und Pre-Prozessoren bereitgestellt, damit sich die Konfiguration in die bestehende Landschaft einbettete. Die Konzepte von Visual Studio Vorlagen können in diesen Zusammenhang verwendet werden. Die Bereitstellung mit Pre-Prozessor-Dateien funktioniert jedoch nur mit klassischen .NET Framework Anwendungen und dem Package-Ansatz von NuGet.

Nachfolgend ein Auszug der TestSetup.cs.pp – Datei für NuGet im Package-Format:

using TechTalk.SpecFlow;

namespace $rootnamespace$.Steps
{
    [Binding]
    public class TestSetup
    {
        private static IBrowserDriver browserDriver;
        …
    }
}

Bei der Installation eines NuGet-Pakets werden die Variablen wie $rootnamespace$ durch die Angaben der jeweiligen Anwendung ersetzt.

Wie gehen wir vor mit NuGet PackageReference für .NET Core Anwendungen?

Mit .NET Core wurde in NuGet das PackageReference-Format eingeführt. In diesem Format funktioniert dieser Ansatz nicht mehr. Uns interessierte ein ähnlicher Ansatz für die Bereitstellung in .NET Core, eine Variante fanden wir mit .NET CLI Vorlagen. Beim NuGet – Paket für die Klassenbibliothek mussten wir keine Änderungen vornehmen, lediglich die Vorlagen erforderten einen neuen Ansatz und sind künftig als .NET CLI Template im NuGet-Feed verfügbar.

Der Wechsel zu .NET CLI Vorlagen in vier Schritten

Schritt 1: Anlegen des Projekts

Als erstes erstellten wir eine Beispiel-Projektmappe und fügten unsere Referenzen hinzu. Anschliessend übernahmen wir die Setup-Dateien und Klassen in die Projektmappe. Folgende Abbildung zeigt beispielhaft den Aufbau:

Abbildung zeit den Solution-Explorer des Projekts als Ausgangslage für die .NET CLI Vorlage mit den nötigen Paketreferenzen und Dateien

Schritt 2: .NET CLI Vorlage bereitstellen

Nachdem wir alle Anpassungen vorgenommen hatten, haben wir innerhalb des Projektordners einen Ordner .template.config angelegt. Innerhalb dieses Ordners stellten wir die Datei template.json bereit. Hierbei handelt es sich um eine Konvention für .NET CLI Vorlagen

In der Datei template.json definierten wir die notwendigen Angaben für die Template-Engine:

{
  "$schema": "http://json.schemastore.org/template",
  "author": "databinding.gmbh",
  "classifications": ["Library"],
  "name": "BDD SpecFlow Blogsample Template",
  "shortName": "bddsample",
  "defaultName": "bdd.AcceptanceTests",
  "identity": "bdd.specflow.blogsample.template",
  "tags": {
    "language": "C#",
    "type": "project",
  },
  "sourceName": "BDD.BeispielVorlage",
  "preferNameDirectory": true,
  "symbols": {
    "Framework": {
      "type": "parameter",
      "description": "The target framework for the project.",
      "datatype": "choice",
      "choices": [
		{
          "choice": "netcoreapp3.1",
          "description": "Target netcoreapp3.1"
        },
        {
          "choice": "net5.0",
          "description": "Target net5.0"
        }
      ],
      "replaces": "net5.0",
      "defaultValue": "net5.0"
    }
  }
}

Ein kleines Detail ist die Eigenschaft sourceName. Diese übernimmt die Aufgabe der bekannten Variable $rootnamespace$, die bisher für die Erstellung von Visual Studio Vorlagen den Namespace ersetzte. Dies hat einen grossen Vorteil, die Vorlage bleibt kompilierbar und lässt sich so besser testen und erweitern. Das erleichtert die Wartung und Weiterentwicklung solcher Templates im Vergleich zum alten Ansatz. Das komplette Schema ist auf Schemastore einsehbar.

Ein Beispiel der Struktur des Templates im Solution Explorer:

Abbildung zeigt die Ordner-Struktur als Konvention für die .NET CLI Vorlage

Damit sind bereits alle Tätigkeiten erledigt. Über die Kommandozeile kann das Template installiert werden.

Dazu in das src-Verzeichnis wechseln und folgenden Befehl ausführen:

dotnet new -i .\

War die Installation erfolgreich, dann wird die Vorlage in der Liste der NET CLI Vorlagen aufgelistet:

Abbildung zeigt die erstellte .NET CLI Vorlage in der Liste der installierten Templates

Mithilfe des definierten ShortName kann ein neues Projekt mit folgendem Befehl erstellt werden:

dotnet new bddsample -n TestProjekt
Abbildung zeigt ein Projekt welches auf der .NET CLI Vorlage basiert und der Namespace durch die Anhabe des Projektnamens ersetzt wurde.

Soll das Template wieder entfernt werden, so geht das mit dem Befehl:

dotnet new -u Pfad\src\databinding.bdd.Template

Wenn die Vorlage über ein Verzeichnis wie in diesem Fall installiert wurde, dann empfiehlt sich für die Deinstallation der absolute Pfad zum Verzeichnis. Mit dem Befehl:

dotnet new -l

können alle installierten Templates aufgelistet werden. So lässt sich prüfen, ob die Deinstallation erfolgreich war.

Schritt 3: NuGet-Paket erstellen

Damit diese Vorlage via NuGet installierbar ist, müssen wir eine nuspec-Datei mit folgendem Inhalt erstellen:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>bdd.specflow.blogsample.template</id>
    <version>$version$</version>
    <description>
      NET CLI Sample
    </description>
    <authors>René Leupold</authors>
    <license type="expression">MIT</license>
    <packageTypes>
      <packageType name="Template" />
    </packageTypes>
  </metadata>
  <files>
    <file src="BDD.BeispielVorlage\**\*.*" exclude="BDD.BeispielVorlage\**\bin\**\*.*;BDD.BeispielVorlage\**\obj\**\*.*" target="Content" />
  </files>  
</package>

Als Id wird der Inhalt von identity aus dem template.json übernommen. Der Unterschied dieser nuspec-Datei liegt im Element packageType. Bei Angabe des Werts Template ist als target das Content-Verzeichnis wieder von Bedeutung. Mit der Zeile file werden alle Dateien im Ordner BDD.BeispielVorlage, ausgenommen des Inhalts vom bin und obj-Verzeichnis, hinzugefügt. Damit sind die Arbeiten für die Bereitstellung via NuGet erledigt. Mit den Anweisungen nuget pack and push ist das Paket sehr schnell erstellt. Interessant dabei ist die angepasste Hilfestellung, die NuGet.org für die Installation bietet. Durch den PackageType Template ändert sich die Installationssyntax.

Abbildung zeigt die angepasste Installationsanweisung für unsere .NET CLI Vorlage auf NuGet

Damit steht die Vorlage für die Kommandozeile bereit. Ideal wäre es, wenn diese auch über den Wizard von Visual Studio verfügbar wäre, um auf unterschiedliche Arbeitsweisen der Teammitglieder eingehen zu können.

Schritt 4: Bereitstellung für Visual Studio

Ab der Version 16.8 stellt Visual Studio für dieses Szenario ein Preview-Feature bereit. Unter Options/Environment/Preview Feature lässt sich die Funktionalität «Show all .NET Core templates in the New project dialog» aktivieren.

Abbildung zeigt die Oberfläche in Visual Studio, wo die Einstellung aktiviert werden muss, damit .NET CLI Vorlagen im Projektassistenten verfügbar sind.

Für unsere Vorlage stellen wir in diesem Zusammenhang im Verzeichnis .template.config eine weitere Datei ide.host.json mit folgenden Inhalt bereit, welche als Ergänzung für den Projekt-Wizard dient:

{
  "$schema": "http://json.schemastore.org/vs-2017.3.host",
  "icon": "icon.png",
  "symbolInfo": [
    {
      "id": "AuthorName",
      "name": {
        "text": "databinding.gmbh"
      },
      "isVisible": "true"
    },
    {
      "id": "Description",
      "name": {
        "text": "BDD Template for Acceptance Tests with SpecFlow, SpecRun, Selenium, Browserstack and LivingDoc"
      },
      "isVisible": "true"
    }
  ]
}

Nach der Aktualisierung und Installation des NuGet-Pakets öffnen wir Visual Studio und sehen unsere Vorlage in der Auswahlliste.

Abbildung zeigt die .NET CLI Vorlage in der Visual Studio Projekt-Auswahl

Nach der Auswahl startet hier der Wizard mit Angaben zum Projekt.

Abbildung zeigt den zweiten Schritt des Assistenten zum Anlegen des Projekts

Im nächsten Schritt greifen auch die Einstellungen, welche in der Konfiguration in template.json hinterlegt sind.

Abbildung zeigt den Assistenzschritts zur Auswahl des Ziel-Frameworks

Nach der Erstellung steht die transformierte Vorlage als Arbeitsgrundlage bereit. Interessant dabei ist, dass diese Vorlagen ebenfalls im Dialog von JetBrains Rider zur Verfügung stehen.

Abbildung zeigt die Vorlage im Projektassistenten der IDE JetBrains Rider

Fazit

In Kombination mit NuGet nutzten wir bisher in Full-Framework Projekten die Pre-Prozessor Dateien mit den typischen Platzhalter-Variablen wie $rootnamespace$ oder $filename$. Ein grosser Nachteil dieser Platzhalter-Variablen ist der Bruch der Code-Dateien in der Entwicklungsumgebung. Die Projektmappe kompiliert nicht mehr in Visual Studio, was den Aufwand bei Pflege und Weiterentwicklung dieser Templates erhöht. Diesen Nachteil lösen die NET CLI – Vorlagen mit dem Verzeichnis-Ansatz .template.config und den Eigenschaften wie sourceName.

Ein weiterer Vorteil der NET CLI Vorlagen ist die unabhängige Verwendung in der Kommandozeile oder mit dem Projekt Wizard in Visual Studio. Da so besser auf die unterschiedlichen Arbeitsweisen eingegangen werden kann, ist die Gefahr von Blockadehaltungen und unabgestimmten Insellösungen geringer. Die Tatsache, dass auch die IDE JetBrains Rider die Vorlagen im Projektdialog unterstützt, kann zusätzlich dazu beitragen, dass sie als Rahmenbedingungen und Grundstruktur im Team akzeptiert werden.

Das hier gezeigte Beispiel ist auf GitHub verfügbar.