Wie ein QS-Techniker einen ganzen Tag gespart hat, indem er AutoTests in Visual Studio und Test IT verknüpft hat

Die modernen Arbeitstools des Entwicklers sind ein Dutzend verschiedener Anwendungen: eine IDE, ein Testsystem, verschiedene Frameworks, Versionskontroll- und Containerisierungssysteme, Büroanwendungen und vieles mehr.

Und oft verbringen wir, ohne es zu merken, wertvolle Zeit damit, Daten von einem funktionierenden System auf ein anderes zu übertragen. Aber warum nehmen wir die Optimierung des Workflows nicht auch bei kleinen Dingen auf? Fünf Minuten, multipliziert mit fünfmal am Tag, ergeben insgesamt mehr als einen Arbeitstag pro Monat, was viel nützlicher ist als die Durchführung von Routinearbeiten. Daher haben wir eine Erweiterung für Visual Studio erstellt, mit der wir den Prozess der Erstellung automatischer Tests in unserem Test-IT-System automatisieren können.

Fortsetzung der Geschichte von Webhooksund wie sie dazu beitragen, viele Dienste bei der Arbeit miteinander zu verbinden, präsentieren wir Ihnen unsere Geschichte über das Erstellen einer Erweiterung für unsere funktionierende IDE - Visual Studio. Willkommen bei Katze!

Dieser Artikel ist ein Gastbeitrag der Jungs von Test IT.



Der im vorherigen Artikel beschriebene Chatbot ist natürlich gut, dient aber bisher nur dazu, Informationen über den aktuellen Status von Autotests zu erhalten. Wenn wir eine engere Integration in das Test-IT-System konfigurieren, können wir die Liste der Tests des aktuellen Projekts im Portal automatisch erweitern, ohne sie manuell hinzufügen zu müssen.

Zuvor verwendeten wir dazu eine hastig bereitgestellte Bibliothek und eine Konsolenanwendung, die beim Start die aktuelle Lösung untersuchte, ein Testprojekt fand, eine Liste von Autotests aus ihrer Assembly extrahierte und sie an unsere API sendete. Es scheint, dass Sie es so lassen können, weil die Lösung funktioniert. Es ist jedoch sehr schwierig, ein gesamtes Projekt ständig aufzuschieben (auch wenn es eine Methode ist, die die Bibliothek asynchron aufruft), und die ewigen Abhängigkeiten der Projekte untereinander sind auch kein Zeichen für guten Geschmack.

Also haben wir die Erweiterung für Visual Studio erstellt und werden nun ein wenig darüber erzählen, wie wir sie erstellt haben.

Technische Seite


Wir werden die Erweiterung natürlich in Visual Studio selbst entwickeln. Schließlich kennt niemand die IDE so gut wie die IDE selbst. Stellen Sie zunächst sicher, dass Visual Studio über alle Komponenten verfügt, die zum Erstellen von Erweiterungen erforderlich sind. Wir suchen und starten das Visual Studio-Installationsprogramm und überprüfen das Modul "Entwicklung von Visual Studio-Erweiterungen":



Eine Überprüfung reicht aus, um alle für die Arbeit erforderlichen Bibliotheken zu installieren. Die Informationen zur Installation für die Entwicklung von Erweiterungen von Visual Studio sollten ungefähr der folgenden Situation entsprechen: Der



vorbereitende Teil ist beendet. Wir können mit der Erstellung eines neuen Projekts fortfahren und in die Architektur und Struktur der Erweiterungsvorlage eintauchen.

Wir werden die neue Erweiterung erweitern, indem wir die Liste der verfügbaren Lösungen mit dem Ausdruck "VSIX" suchen und VSIX-Projekt auswählen (natürlich C #):



Nach der Bereitstellung begegnet uns auf den ersten Blick die einfache Architektur der Erweiterungsvorlage:



In diesem Beispiel ist source.extension.vsixmanifest ein allgemeines Manifest, das die grundlegenden Eigenschaften der Erweiterung beschreibt, z. B. Produktname, Autor, Version, Beschreibung, Erweiterungssymbol und Zielversion der IDE und viele andere. Diese Eigenschaften werden übrigens sowohl im Erweiterungsspeicher als auch im Visual Studio-Erweiterungsinstallationsprogramm angezeigt.

VSIXProject3Package.cs (Maske: {ProjectName} Package.cs) ist wiederum eine Initialisierungsklasse, die alle verfügbaren Befehle und Ressourcenschemata registriert. Wir werden jetzt die Teams kennenlernen.

Erstellen wir ein neues Team, das ein WPF-Fenster mit Erweiterungsparametern öffnet: Wir müssen Daten über das Zielprojekt, den Speicherort und den Namen seiner Assembly, die Verbindung zum Server und andere Informationen irgendwo speichern.

Wir suchen nach einem neuen Element, das als logischer Befehl bezeichnet wird .



Viele mögen sich fragen: Warum verwenden wir Command, wenn das asynchrone Werkzeugfenster und das Werkzeugfenster für die Beschreibung der Aufgabe entsprechend dem Namen geeignet sind? Alles ist ganz einfach: Beim Erstellen dieser beiden Elemente stellen wir eine Markup-Datei für xaml bereit, eine CS-Datei mit demselben Namen und einen Befehl. Alles wäre in Ordnung, so wie wir es brauchen, aber in diesem Fall stellt das Tool-Fenster (egal ob es asynchron ist) eine Fenstervorlage bereit, die in Visual Studio selbst integriert ist. Bei der Ausgabe erhalten wir eines der Fenster, das standardmäßig anstelle des Debug-Fensters erweitert wird. Natürlich ist das alles anpassbar, aber wir brauchen ein Fenster von Drittanbietern. Aus diesem Grund verwenden wir nicht das Werkzeugfenster, sondern fügen ein reguläres WPF-Fenster hinzu.

Ich denke, die Details zum Erstellen eines neuen Fensters mit Standardelementen können weggelassen werden, da es langweilig, uninteressant und schwach mit dem Titel des Artikels verbunden ist. Die einzige Empfehlung, die Sie hier hinterlassen können: Visual Studio zeigt beim Hinzufügen eines neuen Elements das WPF-Fenster nicht in der Liste der Optionen an. Die schnellste und einfachste Option besteht darin, ein solches Fenster separat vom Erweiterungsprojekt zu erstellen und es dann in das aktuelle Projekt zu übertragen (vergessen Sie nicht, den Speicherplatz zu korrigieren Namen).



Nach dem Erstellen eines neuen Teams, das wir OpenSettingsCommand genannt haben, erstellt die legendäre Studio-Codegenerierung eine Teamklasse und eine Datei im vsct-Format, in der sowohl das Markup unserer Erweiterung als auch die Zuordnung von Befehlen und den Schaltflächen, die sie aufrufen, gespeichert werden. Es ist sehr wünschenswert, das Markup in Ihrer eigenen Implementierung neu zu schreiben: Es wird automatisch erstellt und platziert Ihre Elemente im Menü "Erweiterungen". In diesem Fall haben wir dieses Markup neu geschrieben, indem wir eine Gruppe von zwei Teams erstellt und direkt in der Symbolleiste platziert haben. Ein Beispiel für einen Befehl und ein Markup finden Sie in unserem Repository .

In der erstellten Klasse können wir die Execute-Methode bemerken, mit der alles beginnt, wenn dieser Befehl aufgerufen wird. Hier registrieren wir die Initialisierung des neuen WPF-Fensters.

Envdte


Und wir näherten uns reibungslos der Verwendung des Visual Studio SDK, nämlich der EnvDTE-Bibliothek. Mit dieser COM-Bibliothek können wir mit Objekten und Elementen von Visual Studio arbeiten: eine Liste aktiver Lösungen und Projekte abrufen, mit Syntaxhervorhebung, aktiven Fenstern arbeiten, Projektcode lesen und vieles mehr. Wenn Sie in die Dokumentation dieser Bibliothek eintauchen, finden Sie in der Tat viele nützliche Funktionen für sich. In diesem Beispiel verwenden wir es genau, um eine Liste der Projekte in einer aktiven Lösung zu erhalten.

Trotz der kleinen freien Codebasis benötigen wir nur wenige Codezeilen, um eine Liste der Projekte in der aktiven Lösung zu erhalten:

ThreadHelper.ThrowIfNotOnUIThread();
var activeVS = (DTE)Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(DTE))
 ?? throw new InvalidOperationException("DTE not found");
var activeProjects = new List<Project>();
activeProjects.AddRange(activeVS.Solution.Projects.Cast<Project>());
//  , activeProjects         

So einfach und natürlich können wir detaillierte Informationen über alle aktiven Projekte sammeln.

Nehmen Sie aus dieser Liste die Namen der Projekte (Eigenschaftsname) und den Pfad zu den csproj-Dateien (plötzlich FullName): Dies reicht völlig aus, um den Benutzer nach dem gewünschten Projekt aus der Liste der möglichen zu fragen und es dem Verzeichnis für die Suche nach Assemblys zuzuordnen.

Der nächste Schritt besteht darin, die Bibliothek zu verbinden, deren Aufgabe darin besteht, die Baugruppe zu analysieren, Autotests zu sammeln und auf dem Test-IT-Portal zu veröffentlichen. Wir werden die Details zum Erstellen der Bibliothek weglassen, aber wir werden eine interessante Klasse bereitstellen, die die Liste der Autotests von der geladenen Assembly abrufen kann:

public class AutotestsService
    {
        public IList<AutotestModel> GetAutotestsFromAssembly<TTestClassAttribute, TTestMethodAttribute>(Assembly assembly, Guid projectId, string repositoryLink)
            where TTestClassAttribute : Attribute
            where TTestMethodAttribute : Attribute
        {
            MethodInfo[] testMethods = GetAutotestFromAssembly<TTestClassAttribute, TTestMethodAttribute>(assembly);

            List<AutotestModel> allModels = new List<AutotestModel>();
            foreach (MethodInfo method in testMethods)
            {
                AutotestModel autotest = new AutotestModel()
                {
                    ExternalId = method.Name,
                    LinkToRepository = repositoryLink,
                    ProjectId = projectId,
                    Name = GetAutotestName(method.Name),
                    Classname = method.DeclaringType.Name,
                    Namespace = GetAutotestNamespace(method)
                };

                allModels.Add(autotest);
            }

            return allModels;
        }

        private static MethodInfo[] GetAutotestFromAssembly<TTestClassAttribute, TTestMethodAttribute>(Assembly assembly)
            where TTestClassAttribute : Attribute
            where TTestMethodAttribute : Attribute
        {
            return assembly.GetTypes()
                .Where(c => c.IsDefined(typeof(TTestClassAttribute)))
                .SelectMany(t => t.GetMethods())
                .Where(m => m.IsDefined(typeof(TTestMethodAttribute)))
                .ToArray();
        }

        private string GetAutotestName(string autotestExternalId)
        {
            StringBuilder autotestName = new StringBuilder();

            for (int i = 0; i < autotestExternalId.Length; i++)
            {
                if (char.IsUpper(autotestExternalId[i]) && i != 0)
                    autotestName.Append(' ');
                autotestName.Append(autotestExternalId[i]);
            }

            return autotestName.ToString();
        }

        private string GetAutotestNamespace(MethodInfo testMethod)
        {
            return testMethod.DeclaringType.FullName
                .Replace($".{testMethod.DeclaringType.Name}", string.Empty);
        }
    }

Der gesamte Quellcode der Bibliothek kann immer in unserem Repository angezeigt werden .

Also zurück zur Erweiterung. Um die Logik unserer Bibliothek zu starten, müssen Sie einen neuen Befehl hinzufügen, in dessen Execute-Methode wir den Bibliotheksaufruf schreiben und ihm die Attribute der Testklassen und -methoden sowie die gespeicherten Erweiterungsparameter übergeben:

var settings = Properties.Settings.Default;
var executor = new LinkExecutor();
await executor.Execute<TestClassAttribute, TestMethodAttribute>(
    settings.Domain,
    settings.SecretKey,
    settings.ProjectNameInTestIT,
    settings.RepositoryLink ?? string.Empty,
    settings.AssemblyPath,
    Logger);

Wichtiger Hinweis: Wir überspringen den Logger in der Bibliothek, um technische Informationen direkt in das Nachrichtenausgabefenster von Visual Studio schreiben zu können. Die Bibliothek sollte nicht nur an diese IDE gebunden sein, es ist wichtig, dass wir die Möglichkeit lassen, sie in jeder Situation zu verwenden.

Ergebnisse


Als Ergebnis haben wir nach der Entwicklung so etwas wie diese Erweiterung erhalten:



. , Visual Studio, , Visual Studio Visual Studio Visual Studio Visual Studio. , .



Öffnen Sie eine Beispiellösung mit einem Projekt mit Unit-Tests und testen Sie unsere Erweiterung darauf. Beim Laden eines Projekts wird sofort eine neue Schaltfläche im Bedienfeld angezeigt:



Nehmen Sie sofort den geheimen API-Schlüssel von Ihrem persönlichen Konto und bereiten Sie ein neues Projekt auf unserer Plattform vor:





Als Nächstes kehren wir zur Erweiterung zurück. Öffnen Sie unser Fenster mit den Erweiterungsparametern und füllen Sie die Felder aus:



Wir haben die Liste der aktuellen Projekte an den Fensterdesigner übergeben, der freundlicherweise von der Visual Studio SDK-Bibliothek bereitgestellt wurde, und nach Auswahl des Projekts die Optionen "Projekt-DLL" geladen: Der Dienst hat in der Projektassembly "UnitTestProject" nach allen DLL-Dateien gesucht Und zeigte uns mögliche Bibliotheksoptionen. Wir speichern die Einstellungen und führen die Hauptfunktionalität unserer Erweiterung aus.

Im Ausgabefenster sehen wir nach einigen Sekunden Folgendes:



Wen interessiert es, wie wir Nachrichten ausgegeben haben - den Quellcode des Loggers finden Sie hier .

In unserem Beispiel gab es 3 Unit-Tests und 3 Integrationstests. Klingt nach der Wahrheit. Überprüfen Sie, ob ein Portal vorhanden ist:



Fazit


Heute haben wir die Grundlagen zum Erstellen von Erweiterungen für Visual Studio am Beispiel einer Erweiterung zum Veröffentlichen einer Liste von Autotests auf der Test IT-Plattform untersucht. Erweiterungsoptionen sind nur durch Ihre Vorstellungskraft begrenzt: Sie können einen Chat mit einem Team implementieren, Sie können Benachrichtigungen für den Fall eines Projektausfalls in Ihrer Filiale erstellen, Sie können dem Büro sogar einen Pizza-Bestellknopf hinzufügen , wie in unseren Fantasien über das Bot Framework .

Träumen, kreieren, Zeit sparen und immer kreative Spezialisten bleiben!

Über den Autor



Mikhail Pirogovsky ist ein .NET-Entwickler. Das Material wurde mit dem Test-IT-Team geschrieben. In unserer Gruppe auf Facebook sprechen wir über QS-Arbeit, Tests, Tools und mehr.

All Articles