Формирование и компиляция кода, а также соглашения об именовании в Microsoft Fakes

В этой статье рассматриваются проблемы и параметры, связанные с созданием и компиляцией кода Fakes, а также описаны соглашения об именовании для типов, элементов и параметров, создаваемых в Fakes.

Requirements

  • Visual Studio Enterprise

  • Проект .NET Framework

  • Поддержка проектов в стиле пакета SDK, .NET Core и .NET 5.0, представленная в Visual Studio 2019 (обновление 6), включена по умолчанию в обновлении 8. Дополнительные сведения см. в статье Microsoft Fakes для проектов .NET Core и проектов в стиле SDK.

Создание и компиляция кода

Настройка формирования кода заглушек

Создание типов заглушек настраивается в XML-файле с расширением FAKES. Платформа Fakes интегрируется в процесс сборки с помощью пользовательских задач MSBuild и обнаруживает эти файлы во время сборки. Генератор кода Fakes компилирует типы заглушек в сборку и добавляет ссылку на проект.

Следующий пример иллюстрирует типы заглушек, определенные в FileSystem.dll:

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
    <Assembly Name="FileSystem"/>
</Fakes>

Фильтрация типов

Фильтры можно задать в файле FAKES, чтобы ограничить перечень типов, которые должны быть заменены заглушками. Можно добавить неограниченное число элементов Clear, Add, Remove в элемент StubGeneration, чтобы сформировать список выбранных типов.

Например, следующий файл FAKES создает заглушки для типов в пространствах имен System и System.IO, но исключает любой тип, содержащий Handle в пространстве имен System:

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
  <Assembly Name="mscorlib" />
  <!-- user code -->
  <StubGeneration>
    <Clear />
    <Add Namespace="System!" />
    <Add Namespace="System.IO!"/>
    <Remove TypeName="Handle" />
  </StubGeneration>
  <!-- /user code -->
</Fakes>

В строках фильтра используется простая грамматика для определения того, как именно следует выполнять сопоставление:

  • По умолчанию фильтры не учитывают регистр, выполняется сравнение подстрок:

    el соответствует значению hello

  • Добавление ! в конец фильтра обеспечивает точное совпадение с учетом регистра:

    el! не соответствует значению hello

    hello! соответствует значению hello

  • Добавление * в конец фильтра обеспечивает соответствие префиксу строки:

    el* не соответствует значению hello

    he* соответствует значению hello

  • Несколько фильтров в списке, разделенных точкой с запятой, объединяются в виде дизъюнкции:

    el;wo соответствует значениям hello и world

Создание заглушек для конкретных классов и виртуальных методов

По умолчанию типы заглушек создаются для всех незапечатанных классов. Типы заглушки можно ограничить абстрактными классами с помощью файла конфигурации FAKES:

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
  <Assembly Name="mscorlib" />
  <!-- user code -->
  <StubGeneration>
    <Types>
      <Clear />
      <Add AbstractClasses="true"/>
    </Types>
  </StubGeneration>
  <!-- /user code -->
</Fakes>

Внутренние типы

Генератор кода Fakes создает типы оболочек и типы-заглушки для типов, которые являются видимыми для созданной сборки Fakes. Чтобы сделать внутренние типы сборки с оболочкой совместимости видимыми для сборки Fakes и тестовой сборки, добавьте атрибуты InternalsVisibleToAttribute в код сборки с оболочкой совместимости, отвечающий за видимость для созданной сборки Fakes и тестовой сборки. Приведем пример:

// FileSystem\AssemblyInfo.cs
[assembly: InternalsVisibleTo("FileSystem.Fakes")]
[assembly: InternalsVisibleTo("FileSystem.Tests")]

Внутренние типы в сборках со строгими именами

Если сборка с оболочкой совместимости имеет строгое имя и требуется доступ к внутренним типам сборки:

  • Как тестовая сборка, так и сборка Fakes должна иметь строгое имя.

  • Добавьте открытые ключи сборки Fakes и тестовой сборки в атрибуты InternalsVisibleToAttribute в сборках с оболочкой совместимости. Вот как будут выглядеть образцы атрибутов в коде сборки с оболочкой совместимости, когда сборка с оболочкой совместимости имеет строгое имя:

    // FileSystem\AssemblyInfo.cs
    [assembly: InternalsVisibleTo("FileSystem.Fakes",
        PublicKey=<Fakes_assembly_public_key>)]
    [assembly: InternalsVisibleTo("FileSystem.Tests",
        PublicKey=<Test_assembly_public_key>)]
    

Если сборка с оболочкой совместимости имеет строгое имя, платформа Fakes автоматически назначает созданной сборке Fakes строгую подпись. Тестовой сборке следует назначить строгую подпись. См. раздел Сборки со строгими именами.

Платформа Fakes использует один и тот же ключ для подписывания всех созданных сборок, поэтому этот фрагмент кода можно использовать в качестве основы для добавления атрибута InternalsVisibleTo для сборки Fakes в код сборки с оболочкой совместимости.

[assembly: InternalsVisibleTo("FileSystem.Fakes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e92decb949446f688ab9f6973436c535bf50acd1fd580495aae3f875aa4e4f663ca77908c63b7f0996977cb98fcfdb35e05aa2c842002703cad835473caac5ef14107e3a7fae01120a96558785f48319f66daabc862872b2c53f5ac11fa335c0165e202b4c011334c7bc8f4c4e570cf255190f4e3e2cbc9137ca57cb687947bc")]

Можно указать другой открытый ключ для сборки Fakes, например ключ, созданный для сборки с оболочкой совместимости, указав полный путь к SNK-файлу, который содержит альтернативный ключ в виде значения атрибута KeyFile в элементе Fakes\CompilationFAKES-файла. Например:

<-- FileSystem.Fakes.fakes -->
<Fakes ...>
  <Compilation KeyFile="full_path_to_the_alternate_snk_file" />
</Fakes>

Затем следует использовать открытый ключ из альтернативного SNK-файла в качестве второго параметра атрибута InternalVisibleTo для сборки Fakes в коде сборки с оболочкой совместимости:

// FileSystem\AssemblyInfo.cs
[assembly: InternalsVisibleTo("FileSystem.Fakes",
    PublicKey=<Alternate_public_key>)]
[assembly: InternalsVisibleTo("FileSystem.Tests",
    PublicKey=<Test_assembly_public_key>)]

В приведенном выше примере значения Alternate_public_key и Test_assembly_public_key могут быть одинаковыми.

Оптимизация времени сборки

Компиляция сборок Fakes может значительно затянуть время сборки. Можно сократить время сборки, создав сборки имитаций для сборки Fakes для системных сборок платформы .NET системы и сторонних сборок в отдельном централизованном проекте. Поскольку такие сборки редко изменяются на компьютере, созданные сборки Fakes можно повторно использовать в других проектах.

Из проектов модульных тестов добавьте ссылку на скомпилированные сборки Fakes, помещенные в FakesAssemblies в папке проекта.

  1. Создайте новую библиотеку классов с версией среды выполнения .NET, соответствующей тестовым проектам. Назовем ее Fakes.Prebuild. Удалите файл class1.cs в проекте, так как он нам не нужен.

  2. Добавьте ссылку на все системные и сторонние сборки, для которых требуется Fakes.

  3. Добавьте файл FAKES для каждой сборки и выполните сборку.

  4. В тестовом проекте

    • Убедитесь в наличии ссылки на библиотеку DLL среды выполнения Fakes:

      %ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\Microsoft.QualityTools.Testing.Fakes.dll

    • Для каждой сборки, для которой вы создали Fakes, добавьте ссылку на соответствующий файл DLL в папке Fakes.Prebuild\FakesAssemblies проекта.

Предотвращение конфликтов имен сборок

В среде Team Build все выходные данные сборки объединяются в одном каталоге. Если Fakes используется в нескольких проектах, может возникнуть ситуация, когда сборки Fakes из разных версий переопределяют друг друга. Например, как сборки Fakes TestProject1 mscorlib.dll из .NET Framework 2.0, так и сборки Fakes TestProject2 mscorlib.dll для .NET Framework 4 будут выводить данные в сборку mscorlib.Fakes.dll.

Чтобы избежать этой проблемы, платформа Fakes должна автоматически создавать учитывающие версию имена сборок Fakes для ссылок, не относящихся к проектам, при добавлении файлов FAKES. Учитывающее версию имя сборки Fakes внедряет номер версии при создании имени сборки Fakes:

В случае со сборкой Fakes MyAssembly и версией 1.2.3.4 имя будет иметь вид MyAssembly.1.2.3.4.Fakes.

Можно изменить или удалить эту версию, изменив атрибут Version элемента Assembly в файле FAKES:

attribute of the Assembly element in the .fakes:
<Fakes ...>
  <Assembly Name="MyAssembly" Version="1.2.3.4" />
  ...
</Fakes>

Соглашения об именовании для Fakes

Соглашения об именовании для типов заглушек и типов оболочек

Пространства имен

  • К пространству имен добавляется суффикс .Fakes.

    Например, System.Fakes пространство имен содержит типы оболочек пространства имен System.

  • Global.Fakes содержит тип оболочки пустого пространства имен.

    Имена типов

  • Префикс оболочки добавляется к имени типа для получения имени типа оболочки.

    Например, ShimExample является типом оболочки типа Example.

  • Префикс заглушки добавляется к имени типа для получения имени типа заглушки.

    Например, StubIExample является типом заглушки типа IExample.

    Аргументы типа и структуры вложенных типов

  • Аргументы универсального типа копируются.

  • Структура вложенного типа копируется для типов оболочек.

Соглашения об именовании для свойства делегата оболочки или поля делегата заглушки

Основные правила для именования полей, начиная с пустого имени:

  • Имя метода добавляется.

  • Если имя метода является явной реализацией интерфейса, точки удаляются.

  • Если метод является универсальным, добавляется Ofn, где n — число аргументов универсального метода.

    Имена особых методов, таких как методы получения или задания свойства, обрабатываются согласно правилам в приведенной ниже таблице.

Чем является метод... Пример Добавление к имени метода
Конструктор .ctor Constructor
Статический конструктор .cctor StaticConstructor
Метод доступа, имя которого состоит из двух частей, разделенных символом "_" (например, методы получения свойств) kind_name (распространенный вариант, однако он не является обязательным согласно ECMA) NameKind, где обе части начинаются с прописных букв и поменяны местами
Метод получения свойства Prop PropGet
Метод задания свойства Prop PropSet
Метод добавления события Add
Метод удаления события Remove
Оператор, состоящий из двух частей op_name NameOp
Например, оператор + op_Add AddOp
Для оператора преобразования добавляется тип возвращаемого значения. T op_Implicit ImplicitOpT

Примечание.

  • Методы получения и задания индексаторов обрабатываются так же, как и методы для свойств. Для индексаторов имя по умолчанию имеет значение Item.
  • Имена типов параметров преобразуются и объединяются.
  • Тип возвращаемого значения игнорируется, если нет неоднозначности перегрузки. При наличии неоднозначности перегрузки тип возвращаемого значения добавляется в конец имени.

Соглашения об именовании для типов параметров

Выдано Добавляемая строка...
ТипT T

Пространство имен, вложенная структура и универсальные регистрации удаляются.
Параметр выводаout T TOut
Параметр ссылкиref T TRef
Тип массиваT[] TArray
Тип многомерного массиваT[ , , ] T3
Тип указателяT* TPtr
Универсальный типT<R1, ...> TOfR1
Аргумент универсального типа!i для типа C<TType> Ti
Аргумент универсального метода!!i для метода M<MMethod> Mi
Вложенный типN.T Добавляется N, затем T

Рекурсивные правила

Рекурсивно применяются следующие правила:

  • Поскольку платформа Fakes использует C# для создания сборок Fakes, любой символ приведет к тому, что недопустимый токен C#, экранируется за «_» (символ подчеркивания).

  • Если результирующее имя конфликтует с любым членом объявляющего типа, используется схема нумерации, заключающаяся в добавлении двухзначного счетчика, начиная со значения 01.

Использование Microsoft Fakes в непрерывной интеграции

Создание сборки Microsoft Fakes

Microsoft Fakes — это функция, доступная исключительно в Visual Studio Enterprise. Таким образом, создание сборок Fakes требует использования задачи сборки Visual Studio при создании проекта.

Примечание.

Альтернативная стратегия включает проверка проверка сборки Поддельных непосредственно в систему непрерывной интеграции (CI) и использование задачи MSBuild. Если вы выбрали этот подход, необходимо включить ссылку на сборку созданной сборки Fakes в тестовом проекте, как показано в следующем фрагменте кода:

<Project Sdk="Microsoft.NET.Sdk">
    <ItemGroup>
        <Reference Include="FakesAssemblies\System.Fakes.dll"/>
    </ItemGroup>
</Project>

Эта ссылка должна быть добавлена вручную, специально для проектов в стиле SDK (т. е. .NET Core, .NET 5+ и платформа .NET Framework), так как эти проекты теперь неявно добавляют ссылки на сборки. Если вы решите использовать этот метод, обязательно обновите сборку Fakes всякий раз, когда родительская сборка проходит изменения.