Современный рабочий инструментарий разработчика представляет собой с десяток самых разных приложений: IDE, тестовая система, различные фреймворки, системы контроля версий и контейнеризации, офисные приложения и многое другое.
И зачастую мы, сами того не замечая, тратим драгоценное время на перенос данных из одной рабочей системы в другую. Но почему бы нам не заняться оптимизацией рабочего процесса, пусть даже в мелочах? Пять минут, умноженные на 5 раз в день, дадут нам в сумме более одного рабочего дня в месяц времени, которое можно потратить куда полезнее, чем на выполнение рутинной работы. Так мы пришли к созданию расширения для Visual Studio, позволяющего нам автоматизировать процесс создания автотестов в нашей системе Test IT.
Продолжая историю про вебхуки и то, как они помогают связывать множество сервисов при работе, представляем вашему вниманию наш рассказ о создании расширения для нашей рабочей IDE – Visual Studio. Добро пожаловать под кат!
![](https://habrastorage.org/r/w1560/webt/de/p4/sn/dep4snssll0ayy6pne0d8g_kvio.png)
Чат-бот, рассматриваемый в предыдущей статье, конечно, хорош, но он пока служит нам только для получения информации о текущем статусе автотестов. А если настроить более плотную интеграцию с системой Test IT, то мы сможем разворачивать список тестов текущего проекта на портале автоматически, без необходимости добавлять их вручную.
Ранее для этого мы использовали наспех развёрнутую библиотеку и консольное приложение, которое при запуске просматривало текущее решение, находило тестовый проект, извлекало из его сборки список автотестов и отправляло их на наш API. Казалось бы, можно так и оставить, ведь решение работает. Но постоянно тягать целый проект (пусть хоть это и один метод, асинхронно вызывающий библиотеку) весьма непросто, да и вечные зависимости проектов между собой тоже не являются признаком хорошего тона.
Так мы пришли к созданию расширения для Visual Studio, и сейчас расскажем немного о том, как мы его сделали.
Разрабатывать расширение мы будем, естественно, в самой Visual Studio. Ведь никто не знает про IDE столько, сколько сама IDE. Для начала убедимся в том, что в Visual Studio установлены все необходимые для создания расширений компоненты. Находим и запускаем Visual Studio Installer и проверяем модуль «Разработка расширений Visual Studio»:
![](https://habrastorage.org/r/w1560/webt/1_/u1/kp/1_u1kppuwwmkcolqbxic8mluadq.png)
Одной галочки нам вполне хватит, чтобы установить все необходимые библиотеки для работы. В сведениях об установке по разработке расширений Visual Studio должна быть примерно следующая ситуация:
![](https://habrastorage.org/r/w1560/webt/f3/8k/ko/f38kkok4jcjusae2fqhmkguplvu.png)
Подготовительная часть на этом окончена, можем переходить к созданию нового проекта и погрузиться в архитектуру и строение шаблона расширения.
Развернём новое расширение, найдя список доступных решений по фразе «VSIX» и выбрав VSIX Project (язык, разумеется, C#):
![](https://habrastorage.org/r/w1560/webt/ms/v4/x0/msv4x0wvkmduxaekbvdxtev5spi.png)
После разворачивания нас встречает, на первый взгляд, несложная архитектура шаблона расширения:
![](https://habrastorage.org/r/w1560/webt/c4/zs/u4/c4zsu4q1ojiogciyo-7s0z6qdsw.png)
В данном примере source.extension.vsixmanifest является обычным манифестом, в котором прописаны основные свойства расширения, такие как имя продукта, автор, версия, описание, иконка расширения, целевая версия IDE и многие другие. К слову, именно эти свойства просматриваются как в магазине расширений, так и установщиком расширений Visual Studio.
VSIXProject3Package.cs (маска: {ProjectName}Package.cs), в свою очередь, является классом-инициализатором, регистрирующим все имеющиеся команды и схему ресурсов. С командами мы сейчас и познакомимся.
Создадим новую команду, которая откроет нам WPF-окно с параметрами расширения: нам необходимо где-то хранить данные о целевом проекте, о местонахождении и названии его сборки, о подключении к серверу и прочую информацию.
Ищем новый элемент, называемый вполне логично Command.
![](https://habrastorage.org/r/w1560/webt/7t/a8/rl/7ta8rlzogkygdh2cxngxbrt8kgi.png)
Многие могут задаться вопросом: почему же мы используем Command, когда под описание задачи, если верить названию, нам подходят элементы Async Tool Window и Tool Window? Всё довольно просто: эти два элемента при создании развернут нам файл разметки на xaml, cs-файл с аналогичным названием, а также команду. Всё бы ничего, вроде нам это и надо, но в данном случае Tool Window (неважно, асинхронный ли он) разворачивает шаблон окна, интегрируемого в саму Visual Studio. На выходе мы получим одно из окон, по-умолчанию развёрнутое вместо окна Debug. Разумеется, всё это настраивается, но нам необходимо стороннее окно. Именно поэтому мы не используем Tool Window, а добавим обычное WPF-окно.
Думаю, подробности создания нового окна со стандартными элементами можно опустить, так как это скучно, неинтересно и слабо соотносится с названием статьи. Единственная рекомендация, которую здесь можно оставить: Visual Studio при добавлении нового элемента не покажет вам в списке вариантов окно WPF, поэтому самый быстрый и простой вариант заключается в создании такого окна отдельно от проекта расширения и его последующем переносе в текущий проект (не забудьте исправить пространство имён).
![](https://habrastorage.org/r/w1560/webt/z8/hf/nf/z8hfnfvt6rebokigeoxdq2hcl-q.png)
Итак, после создания новой команды, которую мы назвали как OpenSettingsCommand, легендарная кодогенерация студии создаёт нам класс команды и файл формата vsct, хранящий в себе как разметку нашего расширения, так и сопоставление команд и вызывающих их кнопок. Разметку крайне желательно переписать на собственную реализацию: созданная автоматически, она разместит ваши элементы в меню «Расширения». В данном случае мы переписали эту разметку, создав группу из двух команд и поместив её прямо на панель инструментов. Пример команды и разметки можно найти у нас в репозитории.
В созданном классе мы можем заметить метод Execute, с которого всё и начинается при вызове данной команды. Собственно, именно здесь мы и пропишем инициализацию нового WPF-окна.
И мы плавно подошли к использованию Visual Studio SDK, а именно – библиотеке EnvDTE. Данная COM библиотека позволяет нам работать с объектами и элементами Visual Studio: получать список активных решений и проектов, работать с подсветкой синтаксиса, активными окнами, читать код проектов и многое другое. На самом деле, если погрузиться в документацию данной библиотеки, то можно обнаружить для себя довольно много полезных функций. В данном примере мы используем её именно для получения списка проектов в активном решении.
Несмотря на небольшую базу кода в свободном доступе, нам хватит всего несколько строк кода, чтобы получить список проектов в активном решении:
Вот так легко и непринуждённо мы можем собрать детальную информацию обо всех активных проектах.
Возьмём с этого списка имена проектов (свойство Name) и пути до csproj-файлов (внезапно, FullName): этого нам вполне хватит, чтобы спросить у пользователя нужный проект из списка возможных и сопоставить ему каталог для поиска сборок.
Следующим шагом будет подключение библиотеки, в задачи которой входит анализ сборки, сбор автотестов и их публикация на портале Test IT. Подробности создания библиотеки мы опустим, но приведём интересный класс, который умеет получать список автотестов из загруженной сборки:
Весь исходный код библиотеки всегда можно посмотреть у нас в репозитории.
Итак, вернёмся к расширению. Для запуска логики нашей библиотеки необходимо добавить новую команду, в методе Execute которой пропишем вызов библиотеки, передав ей атрибуты тестовых классов и методов, а также сохранённые параметры расширения:
Важное замечание: Logger мы прокидываем в библиотеку для возможности писать техническую информацию прямо в окно вывода сообщений Visual Studio. Библиотека не должна быть привязана только к данной IDE, нам важно оставить возможность использовать её в любой ситуации.
В результате после разработки у нас получилось примерно такое расширение:
![](https://habrastorage.org/r/w1560/webt/al/b2/sa/alb2samrb9b5f6l48lmfoumpa2q.png)
![](https://habrastorage.org/r/w1560/webt/jn/1d/zy/jn1dzyk8tj82bfbga47pabgta2g.png)
Откроем пример решения, содержащий проект с Unit-тестами, и опробуем наше расширение на нём. При загрузке проекта сразу можем заметить новую кнопку на панели:
![](https://habrastorage.org/r/w1560/webt/bc/bv/ts/bcbvtslenbgaar8yodstv3b63wu.png)
Сразу возьмём секретный ключ API из личного кабинета и подготовим новый проект на нашей платформе:
![](https://habrastorage.org/r/w1560/webt/to/md/t8/tomdt8mhsgaiqyxu99gmjg3faoe.png)
![](https://habrastorage.org/r/w1560/webt/i8/vt/06/i8vt06nwwm9sjccpcpsnft2hjiw.png)
Далее вернёмся к расширению. Откроем наше окно с параметрами расширения и заполним поля:
![](https://habrastorage.org/r/w1560/webt/dk/k8/7o/dkk87oshczsgho69bwppufbckvm.png)
В конструктор окна мы передавали список текущих проектов, любезно предоставленным библиотекой Visual Studio SDK, а варианты «Project dll» мы подгружали после выбора проекта: сервис выполнил поиск всех dll-файлов в сборке проекта «UnitTestProject» и показал нам возможные варианты библиотек. Сохраняем настройки и запускаем основной функционал нашего расширения.
В окне вывода спустя несколько секунд мы видим следующее:
![](https://habrastorage.org/r/w1560/webt/zm/ds/sy/zmdssyus9a7hapbht4jzi5z_cfc.png)
Кому интересно, как мы сделали вывод сообщений – исходный код логгера можно почитать тут.
В нашем примере было 3 Unit-теста и 3 интеграционных теста. Похоже на правду. Проверим, что там на портале:
![](https://habrastorage.org/r/w1560/webt/5g/mx/fh/5gmxfhgj4hamitcruqipqm__6z4.png)
Сегодня мы рассмотрели основы создания расширений для Visual Studio на примере расширения для публикации списка автотестов на платформе Test IT. Возможности расширений ограничиваются лишь вашей фантазией: можно реализовать чат с командой, можно создать уведомления в случае поломки проекта на вашей ветке, можно даже, как и в наших фантазиях о Bot Framework, добавить кнопку заказа пиццы в офис.
Мечтайте, творите, экономьте время и всегда оставайтесь творческими специалистами!
![](https://habrastorage.org/r/w1560/webt/-a/ih/-6/-aih-6npcjorku37ud5joj5p7no.png)
Михаил Пироговский — .NET разработчик. Материал написан вместе с командой Test IT. В нашей группе на Facebook мы рассказываем про работу в QA, тестирование, инструменты и прочее.
И зачастую мы, сами того не замечая, тратим драгоценное время на перенос данных из одной рабочей системы в другую. Но почему бы нам не заняться оптимизацией рабочего процесса, пусть даже в мелочах? Пять минут, умноженные на 5 раз в день, дадут нам в сумме более одного рабочего дня в месяц времени, которое можно потратить куда полезнее, чем на выполнение рутинной работы. Так мы пришли к созданию расширения для Visual Studio, позволяющего нам автоматизировать процесс создания автотестов в нашей системе Test IT.
Продолжая историю про вебхуки и то, как они помогают связывать множество сервисов при работе, представляем вашему вниманию наш рассказ о создании расширения для нашей рабочей IDE – Visual Studio. Добро пожаловать под кат!
Эта статья — гостевая публикация от ребят из Test IT
![](https://habrastorage.org/webt/de/p4/sn/dep4snssll0ayy6pne0d8g_kvio.png)
Чат-бот, рассматриваемый в предыдущей статье, конечно, хорош, но он пока служит нам только для получения информации о текущем статусе автотестов. А если настроить более плотную интеграцию с системой Test IT, то мы сможем разворачивать список тестов текущего проекта на портале автоматически, без необходимости добавлять их вручную.
Ранее для этого мы использовали наспех развёрнутую библиотеку и консольное приложение, которое при запуске просматривало текущее решение, находило тестовый проект, извлекало из его сборки список автотестов и отправляло их на наш API. Казалось бы, можно так и оставить, ведь решение работает. Но постоянно тягать целый проект (пусть хоть это и один метод, асинхронно вызывающий библиотеку) весьма непросто, да и вечные зависимости проектов между собой тоже не являются признаком хорошего тона.
Так мы пришли к созданию расширения для Visual Studio, и сейчас расскажем немного о том, как мы его сделали.
Техническая сторона
Разрабатывать расширение мы будем, естественно, в самой Visual Studio. Ведь никто не знает про IDE столько, сколько сама IDE. Для начала убедимся в том, что в Visual Studio установлены все необходимые для создания расширений компоненты. Находим и запускаем Visual Studio Installer и проверяем модуль «Разработка расширений Visual Studio»:
![](https://habrastorage.org/webt/1_/u1/kp/1_u1kppuwwmkcolqbxic8mluadq.png)
Одной галочки нам вполне хватит, чтобы установить все необходимые библиотеки для работы. В сведениях об установке по разработке расширений Visual Studio должна быть примерно следующая ситуация:
![](https://habrastorage.org/webt/f3/8k/ko/f38kkok4jcjusae2fqhmkguplvu.png)
Подготовительная часть на этом окончена, можем переходить к созданию нового проекта и погрузиться в архитектуру и строение шаблона расширения.
Развернём новое расширение, найдя список доступных решений по фразе «VSIX» и выбрав VSIX Project (язык, разумеется, C#):
![](https://habrastorage.org/webt/ms/v4/x0/msv4x0wvkmduxaekbvdxtev5spi.png)
После разворачивания нас встречает, на первый взгляд, несложная архитектура шаблона расширения:
![](https://habrastorage.org/webt/c4/zs/u4/c4zsu4q1ojiogciyo-7s0z6qdsw.png)
В данном примере source.extension.vsixmanifest является обычным манифестом, в котором прописаны основные свойства расширения, такие как имя продукта, автор, версия, описание, иконка расширения, целевая версия IDE и многие другие. К слову, именно эти свойства просматриваются как в магазине расширений, так и установщиком расширений Visual Studio.
VSIXProject3Package.cs (маска: {ProjectName}Package.cs), в свою очередь, является классом-инициализатором, регистрирующим все имеющиеся команды и схему ресурсов. С командами мы сейчас и познакомимся.
Создадим новую команду, которая откроет нам WPF-окно с параметрами расширения: нам необходимо где-то хранить данные о целевом проекте, о местонахождении и названии его сборки, о подключении к серверу и прочую информацию.
Ищем новый элемент, называемый вполне логично Command.
![](https://habrastorage.org/webt/7t/a8/rl/7ta8rlzogkygdh2cxngxbrt8kgi.png)
Многие могут задаться вопросом: почему же мы используем Command, когда под описание задачи, если верить названию, нам подходят элементы Async Tool Window и Tool Window? Всё довольно просто: эти два элемента при создании развернут нам файл разметки на xaml, cs-файл с аналогичным названием, а также команду. Всё бы ничего, вроде нам это и надо, но в данном случае Tool Window (неважно, асинхронный ли он) разворачивает шаблон окна, интегрируемого в саму Visual Studio. На выходе мы получим одно из окон, по-умолчанию развёрнутое вместо окна Debug. Разумеется, всё это настраивается, но нам необходимо стороннее окно. Именно поэтому мы не используем Tool Window, а добавим обычное WPF-окно.
Думаю, подробности создания нового окна со стандартными элементами можно опустить, так как это скучно, неинтересно и слабо соотносится с названием статьи. Единственная рекомендация, которую здесь можно оставить: Visual Studio при добавлении нового элемента не покажет вам в списке вариантов окно WPF, поэтому самый быстрый и простой вариант заключается в создании такого окна отдельно от проекта расширения и его последующем переносе в текущий проект (не забудьте исправить пространство имён).
![](https://habrastorage.org/webt/z8/hf/nf/z8hfnfvt6rebokigeoxdq2hcl-q.png)
Итак, после создания новой команды, которую мы назвали как OpenSettingsCommand, легендарная кодогенерация студии создаёт нам класс команды и файл формата vsct, хранящий в себе как разметку нашего расширения, так и сопоставление команд и вызывающих их кнопок. Разметку крайне желательно переписать на собственную реализацию: созданная автоматически, она разместит ваши элементы в меню «Расширения». В данном случае мы переписали эту разметку, создав группу из двух команд и поместив её прямо на панель инструментов. Пример команды и разметки можно найти у нас в репозитории.
В созданном классе мы можем заметить метод Execute, с которого всё и начинается при вызове данной команды. Собственно, именно здесь мы и пропишем инициализацию нового WPF-окна.
EnvDTE
И мы плавно подошли к использованию Visual Studio SDK, а именно – библиотеке EnvDTE. Данная COM библиотека позволяет нам работать с объектами и элементами Visual Studio: получать список активных решений и проектов, работать с подсветкой синтаксиса, активными окнами, читать код проектов и многое другое. На самом деле, если погрузиться в документацию данной библиотеки, то можно обнаружить для себя довольно много полезных функций. В данном примере мы используем её именно для получения списка проектов в активном решении.
Несмотря на небольшую базу кода в свободном доступе, нам хватит всего несколько строк кода, чтобы получить список проектов в активном решении:
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 теперь хранит в себе список проектов в активном решении
Вот так легко и непринуждённо мы можем собрать детальную информацию обо всех активных проектах.
Возьмём с этого списка имена проектов (свойство Name) и пути до csproj-файлов (внезапно, FullName): этого нам вполне хватит, чтобы спросить у пользователя нужный проект из списка возможных и сопоставить ему каталог для поиска сборок.
Следующим шагом будет подключение библиотеки, в задачи которой входит анализ сборки, сбор автотестов и их публикация на портале Test IT. Подробности создания библиотеки мы опустим, но приведём интересный класс, который умеет получать список автотестов из загруженной сборки:
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);
}
}
Весь исходный код библиотеки всегда можно посмотреть у нас в репозитории.
Итак, вернёмся к расширению. Для запуска логики нашей библиотеки необходимо добавить новую команду, в методе Execute которой пропишем вызов библиотеки, передав ей атрибуты тестовых классов и методов, а также сохранённые параметры расширения:
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);
Важное замечание: Logger мы прокидываем в библиотеку для возможности писать техническую информацию прямо в окно вывода сообщений Visual Studio. Библиотека не должна быть привязана только к данной IDE, нам важно оставить возможность использовать её в любой ситуации.
Результаты
В результате после разработки у нас получилось примерно такое расширение:
![](https://habrastorage.org/webt/al/b2/sa/alb2samrb9b5f6l48lmfoumpa2q.png)
Запустим на отладку наше расширение. Для тех, кто никогда не работал с расширениями Visual Studio, будет немного удивительным тот факт, что при отладке расширения для Visual Studio в Visual Studio будет запускаться среда Visual Studio с установленным в него расширением Visual Studio. Звучит так же страшно, как и логично.
![](https://habrastorage.org/webt/jn/1d/zy/jn1dzyk8tj82bfbga47pabgta2g.png)
Откроем пример решения, содержащий проект с Unit-тестами, и опробуем наше расширение на нём. При загрузке проекта сразу можем заметить новую кнопку на панели:
![](https://habrastorage.org/webt/bc/bv/ts/bcbvtslenbgaar8yodstv3b63wu.png)
Сразу возьмём секретный ключ API из личного кабинета и подготовим новый проект на нашей платформе:
![](https://habrastorage.org/webt/to/md/t8/tomdt8mhsgaiqyxu99gmjg3faoe.png)
![](https://habrastorage.org/webt/i8/vt/06/i8vt06nwwm9sjccpcpsnft2hjiw.png)
Далее вернёмся к расширению. Откроем наше окно с параметрами расширения и заполним поля:
![](https://habrastorage.org/webt/dk/k8/7o/dkk87oshczsgho69bwppufbckvm.png)
В конструктор окна мы передавали список текущих проектов, любезно предоставленным библиотекой Visual Studio SDK, а варианты «Project dll» мы подгружали после выбора проекта: сервис выполнил поиск всех dll-файлов в сборке проекта «UnitTestProject» и показал нам возможные варианты библиотек. Сохраняем настройки и запускаем основной функционал нашего расширения.
В окне вывода спустя несколько секунд мы видим следующее:
![](https://habrastorage.org/webt/zm/ds/sy/zmdssyus9a7hapbht4jzi5z_cfc.png)
Кому интересно, как мы сделали вывод сообщений – исходный код логгера можно почитать тут.
В нашем примере было 3 Unit-теста и 3 интеграционных теста. Похоже на правду. Проверим, что там на портале:
![](https://habrastorage.org/webt/5g/mx/fh/5gmxfhgj4hamitcruqipqm__6z4.png)
Заключение
Сегодня мы рассмотрели основы создания расширений для Visual Studio на примере расширения для публикации списка автотестов на платформе Test IT. Возможности расширений ограничиваются лишь вашей фантазией: можно реализовать чат с командой, можно создать уведомления в случае поломки проекта на вашей ветке, можно даже, как и в наших фантазиях о Bot Framework, добавить кнопку заказа пиццы в офис.
Мечтайте, творите, экономьте время и всегда оставайтесь творческими специалистами!
Об авторе
![](https://habrastorage.org/webt/-a/ih/-6/-aih-6npcjorku37ud5joj5p7no.png)
Михаил Пироговский — .NET разработчик. Материал написан вместе с командой Test IT. В нашей группе на Facebook мы рассказываем про работу в QA, тестирование, инструменты и прочее.