Comment un ingénieur QA a sauvé une journée entière en liant les tests automatiques dans Visual Studio et Test IT

Les outils de travail modernes du développeur sont une douzaine d'applications différentes: un IDE, un système de test, divers frameworks, des systèmes de contrôle de version et de conteneurisation, des applications bureautiques et bien plus encore.

Et souvent, sans le remarquer, nous passons un temps précieux à transférer des données d'un système fonctionnel à un autre. Mais pourquoi ne reprenons-nous pas l'optimisation du workflow, même dans les petites choses? Cinq minutes, multipliées par 5 fois par jour, nous donneront un total de plus d'une journée de travail par mois, temps qui peut être consacré beaucoup plus utile que l'exécution de travaux de routine. Nous sommes donc arrivés à la création d'une extension pour Visual Studio, qui nous permet d'automatiser le processus de création de tests automatiques dans notre système IT de test.

Poursuivre l'histoire des webhookset comment ils aident à connecter de nombreux services au travail, nous vous présentons notre histoire sur la création d'une extension pour notre IDE de travail - Visual Studio. Bienvenue chez cat!

Cet article est un article invité des gars de Test IT.



Le chatbot discuté dans l'article précédent est, bien sûr, bon, mais jusqu'à présent, il nous sert uniquement à obtenir des informations sur l'état actuel des autotests. Et si nous mettons en place une intégration plus étroite avec le système IT de test, nous pouvons étendre la liste des tests du projet en cours sur le portail automatiquement, sans avoir besoin de les ajouter manuellement.

Auparavant, pour cela, nous avons utilisé une bibliothèque déployée à la hâte et une application console qui, au démarrage, a examiné la solution actuelle, trouvé un projet de test, extrait une liste d'autotests de son assemblage et les a envoyées à notre API. Il semblerait que vous puissiez le laisser ainsi, car la solution fonctionne. Mais tergiverser constamment un projet entier (même si c'est une méthode qui appelle la bibliothèque de manière asynchrone) est très difficile, et les dépendances éternelles des projets entre eux ne sont pas non plus un signe de bon goût.

Nous sommes donc arrivés à la création de l'extension pour Visual Studio, et maintenant nous allons en dire un peu plus sur la façon dont nous l'avons faite.

Côté technique


Nous allons naturellement développer l'extension dans Visual Studio lui-même. Après tout, personne ne connaît autant l'IDE que l'IDE lui-même. Tout d'abord, assurez-vous que Visual Studio possède tous les composants nécessaires à la création d'extensions. Nous trouvons et lançons Visual Studio Installer et vérifions le module "Développement d'extensions Visual Studio":



Une seule vérification nous suffit pour installer toutes les bibliothèques nécessaires au travail. Les informations sur l'installation pour le développement d'extensions de Visual Studio devraient être approximativement la situation suivante: La



partie préparatoire est terminée, nous pouvons procéder à la création d'un nouveau projet et nous immerger dans l'architecture et la structure du modèle d'extension.

Nous allons étendre la nouvelle extension en trouvant la liste des solutions disponibles en utilisant la phrase "VSIX" et en choisissant VSIX Project (C #, bien sûr):



Après le déploiement, nous voyons, à première vue, l'architecture simple du modèle d'extension:



dans cet exemple, source.extension.vsixmanifest est un manifeste commun qui décrit les propriétés de base de l'extension, telles que le nom du produit, l'auteur, la version, la description, l'icône d'extension, la version cible de l'IDE et plein d'autres. À propos, ce sont ces propriétés qui sont affichées à la fois dans le magasin d'extensions et dans le programme d'installation de Visual Studio.

VSIXProject3Package.cs (mask: {ProjectName} Package.cs), à son tour, est une classe d'initialisation qui enregistre toutes les commandes et tous les schémas de ressources disponibles. Nous apprendrons à connaître les équipes maintenant.

Créons une nouvelle équipe qui ouvrira une fenêtre WPF avec des paramètres d'extension: nous devons stocker des données sur le projet cible, l'emplacement et le nom de son assemblage, la connexion au serveur et d'autres informations quelque part.

Nous recherchons un nouvel élément, appelé Commandement tout à fait logique .



Beaucoup peuvent se demander: pourquoi utilisons-nous Command lorsque la fenêtre d'outils Async et la fenêtre d'outils conviennent à la description de la tâche, selon le nom? Tout est assez simple: lors de la création de ces deux éléments, nous déploierons un fichier de balisage sur xaml, un fichier cs du même nom, ainsi qu'une commande. Tout irait bien, comme nous en avons besoin, mais dans ce cas, la fenêtre d'outils (peu importe si elle est asynchrone) déploie un modèle de fenêtre qui s'intègre dans Visual Studio lui-même. À la sortie, nous obtenons l'une des fenêtres, qui est développée par défaut au lieu de la fenêtre de débogage. Bien sûr, tout cela est personnalisable, mais nous avons besoin d'une fenêtre tierce. C'est pourquoi nous n'utilisons pas la fenêtre d'outils, mais ajoutons une fenêtre WPF standard.

Je pense que les détails de la création d'une nouvelle fenêtre avec des éléments standard peuvent être omis, car c'est ennuyeux, sans intérêt et faiblement lié au titre de l'article. La seule recommandation que vous pouvez laisser ici: Visual Studio lors de l'ajout d'un nouvel élément ne vous montrera pas la fenêtre WPF dans la liste des options, donc l'option la plus rapide et la plus simple est de créer une telle fenêtre séparément du projet d'extension, puis de la transférer dans le projet en cours (n'oubliez pas de fixer l'espace noms).



Ainsi, après avoir créé une nouvelle équipe, que nous avons appelée OpenSettingsCommand, la génération de code studio légendaire crée une classe d'équipe et un fichier au format vsct qui stocke à la fois le balisage de notre extension et le mappage des commandes et des boutons qui les appellent. Il est hautement souhaitable de réécrire le balisage sur votre propre implémentation: créé automatiquement, il placera vos éléments dans le menu "Extensions". Dans ce cas, nous avons réécrit ce balisage en créant un groupe de deux équipes et en le plaçant directement sur la barre d'outils. Un exemple de commande et de balisage peut être trouvé dans notre référentiel .

Dans la classe créée, nous pouvons remarquer la méthode Execute, avec laquelle tout démarre lorsque cette commande est appelée. En fait, c'est ici que nous enregistrerons l'initialisation de la nouvelle fenêtre WPF.

Envdte


Et nous avons abordé en douceur l'utilisation du SDK Visual Studio, à savoir la bibliothèque EnvDTE. Cette bibliothèque COM nous permet de travailler avec des objets et des éléments de Visual Studio: obtenir une liste des solutions et projets actifs, travailler avec la coloration syntaxique, les fenêtres actives, lire le code du projet et bien plus encore. En fait, si vous plongez dans la documentation de cette bibliothèque, vous pouvez trouver par vous-même pas mal de fonctions utiles. Dans cet exemple, nous l'utilisons précisément pour obtenir une liste de projets dans une solution active.

Malgré la petite base de code gratuite, nous n'avons besoin que de quelques lignes de code pour obtenir une liste de projets dans la solution active:

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         

Si facilement et naturellement, nous pouvons collecter des informations détaillées sur tous les projets actifs.

Prenez dans cette liste les noms de projet (propriété Name) et le chemin vers les fichiers csproj (du coup, FullName): cela nous suffit largement pour demander à l'utilisateur le projet souhaité dans la liste des possibles et le mapper dans le répertoire pour rechercher des assemblys.

L'étape suivante consiste à connecter la bibliothèque, dont les tâches consistent à analyser l'assemblage, collecter les autotests et les publier sur le portail IT Test. Nous allons omettre les détails de la création de la bibliothèque, mais nous fournirons une classe intéressante qui peut obtenir la liste des autotests de l'assembly chargé:

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);
        }
    }

Le code source complet de la bibliothèque peut toujours être consulté dans notre référentiel .

Revenons donc à l'extension. Pour démarrer la logique de notre bibliothèque, vous devez ajouter une nouvelle commande, dans la méthode Execute dont nous écrivons l'appel à la bibliothèque, en lui passant les attributs des classes et méthodes de test, ainsi que les paramètres d'extension enregistrés:

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);

Remarque importante: nous ignorons l'enregistreur dans la bibliothèque pour pouvoir écrire des informations techniques directement dans la fenêtre de sortie des messages de Visual Studio. La bibliothèque ne doit pas être liée uniquement à cet IDE, il est important pour nous de laisser l'opportunité de l'utiliser dans n'importe quelle situation.

résultats


En conséquence, après le développement, nous avons obtenu quelque chose comme cette extension:



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



Ouvrons un exemple de solution contenant un projet avec des tests unitaires et testons notre extension dessus. Lors du chargement d'un projet, nous pouvons immédiatement remarquer un nouveau bouton sur le panneau:



Prenez immédiatement la clé API secrète de votre compte personnel et préparez un nouveau projet sur notre plateforme:





Ensuite, revenons à l'extension. Ouvrons notre fenêtre avec les paramètres d'extension et remplissons les champs:



Nous avons transmis la liste des projets en cours au concepteur de fenêtres, aimablement fournie par la bibliothèque SDK de Visual Studio, et nous avons chargé les options «Project dll» après avoir sélectionné le projet: le service a recherché tous les fichiers dll dans l'assemblage du projet UnitTestProject »Et nous a montré des options de bibliothèque possibles. Nous enregistrons les paramètres et exécutons les principales fonctionnalités de notre extension.

Dans la fenêtre de sortie, après quelques secondes, nous voyons ce qui suit:



Peu importe comment nous avons fait la sortie des messages - le code source de l'enregistreur peut être trouvé ici .

Dans notre exemple, il y a eu 3 tests unitaires et 3 tests d'intégration. Sonne comme la vérité. Vérifiez qu'il existe un portail:



Conclusion


Aujourd'hui, nous avons examiné les bases de la création d'extensions pour Visual Studio en utilisant l'exemple d'une extension pour publier une liste d'autotests sur la plate-forme Test IT. Les options d'extension ne sont limitées que par votre imagination: vous pouvez implémenter un chat avec une équipe, vous pouvez créer des notifications en cas de panne de projet sur votre branche, vous pouvez même, comme dans nos fantasmes sur le Bot Framework , ajouter un bouton de commande de pizza au bureau.

Rêvez, créez, gagnez du temps et restez toujours des spécialistes créatifs!

A propos de l'auteur



Mikhail Pirogovsky est un développeur .NET. Le matériel a été rédigé avec l'équipe Test IT. Dans notre groupe sur Facebook, nous parlons de travailler en AQ, de tests, d'outils et plus encore.

All Articles