Поделиться через


Создание расширений для Microsoft.Testing.Platform

В этой статье рассматриваются точки расширяемости для Microsoft.Testing.Platform за пределами самой тестовой платформы. Сведения о создании платформы тестирования см. в разделе "Создание тестовой платформы".

Полные общие сведения о точке расширения и концепции внутри процесса и вне процесса см. в разделе "Создание пользовательских расширений".

Точки расширяемости

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

Расширения ICommandLineOptionsProvider

Заметка

При расширении этого API настраиваемое расширение будет существовать как в процессе тестового узла, так и вне него.

Как описано в разделе архитектуры , начальный шаг включает создание ITestApplicationBuilder для регистрации тестовой среды и расширений в нем.

var builder = await TestApplication.CreateBuilderAsync(args);

Метод CreateBuilderAsync принимает массив строк (string[]) с именем args. Эти аргументы можно использовать для передачи параметров командной строки всем компонентам платформы тестирования (включая встроенные компоненты, платформы тестирования и расширения), что позволяет настраивать их поведение.

Как правило, переданные аргументы являются теми, которые получены в стандартном методе Main(string[] args). Однако если среда размещения отличается, можно указать любой список аргументов.

Аргументы должны быть префиксированы с двойным --тире. Например: --filter.

Если компонент, например платформа тестирования или точка расширения, хочет предложить настраиваемые параметры командной строки, это можно сделать, реализуя интерфейс ICommandLineOptionsProvider. Затем эту реализацию можно зарегистрировать в ITestApplicationBuilder через фабрику регистрации свойства CommandLine, как показано ниже.

builder.CommandLine.AddProvider(
    static () => new CustomCommandLineOptions());

В приведенном примере CustomCommandLineOptions является реализацией интерфейса ICommandLineOptionsProvider, этот интерфейс состоит из следующих элементов и типов данных:

public interface ICommandLineOptionsProvider : IExtension
{
    IReadOnlyCollection<CommandLineOption> GetCommandLineOptions();

    Task<ValidationResult> ValidateOptionArgumentsAsync(
        CommandLineOption commandOption,
        string[] arguments);

    Task<ValidationResult> ValidateCommandLineOptionsAsync(
        ICommandLineOptions commandLineOptions);
}

public sealed class CommandLineOption
{
    public string Name { get; }
    public string Description { get; }
    public ArgumentArity Arity { get; }
    public bool IsHidden { get; }

    // ...
}

public interface ICommandLineOptions
{
    bool IsOptionSet(string optionName);

    bool TryGetOptionArgumentList(
        string optionName,
        out string[]? arguments);
}

Как отмечалось, ICommandLineOptionsProvider расширяет интерфейс IExtension. Таким образом, как и любое другое расширение, можно включить или отключить его с помощью API IExtension.IsEnabledAsync.

Порядок выполнения ICommandLineOptionsProvider:

Диаграмма, представляющая порядок выполнения интерфейса ICommandLineOptionsProvider.

Рассмотрим api и их среднее значение:

ICommandLineOptionsProvider.GetCommandLineOptions(): этот метод используется для получения всех параметров, предлагаемых компонентом. Каждому CommandLineOption требуется указать следующие свойства:

string name: это имя параметра, представленное без дефиса. Например, фильтра будут использоваться пользователями в качестве --filter.

string description: это описание параметра. Он будет отображаться, когда пользователи передают --help в качестве аргумента построителю приложений.

ArgumentArity arity: Арность параметра — это количество значений, которые можно передать, если указан этот параметр или команда. Доступные степени (арности) в текущий момент:

  • Zero: обозначает арность аргумента равную нулю.
  • ZeroOrOne: представляет арность аргумента ноль или один.
  • ZeroOrMore: представляет арность аргумента ноль или более.
  • OneOrMore: представляет арность аргумента, равную одному или более.
  • ExactlyOne: представляет арность аргумента ровно одного.

Например, см. таблицу arity System.CommandLine .

bool isHidden: это свойство означает, что параметр доступен для использования, но не будет отображаться в описании при вызове --help.

ICommandLineOptionsProvider.ValidateOptionArgumentsAsync. Этот метод используется для проверки аргумента, предоставленного пользователем.

Например, если у вас есть параметр с именем --dop, представляющий степень параллелизма для нашей пользовательской платформы тестирования, пользователь может ввести --dop 0. В этом сценарии значение 0 будет недопустимым, так как ожидается, что он имеет степень параллелизма 1 или более. С помощью ValidateOptionArgumentsAsyncможно выполнить предварительные проверки и при необходимости вернуть сообщение об ошибке.

Возможной реализацией для приведенного выше примера может быть:

public Task<ValidationResult> ValidateOptionArgumentsAsync(
    CommandLineOption commandOption,
    string[] arguments)
{
    if (commandOption.Name == "dop")
    {
        if (!int.TryParse(arguments[0], out int dopValue) || dopValue <= 0)
        {
            return ValidationResult.InvalidTask("--dop must be a positive integer");
        }
    }

    return ValidationResult.ValidTask;
}

ICommandLineOptionsProvider.ValidateCommandLineOptionsAsync: этот метод вызывается как последний и позволяет выполнять глобальную проверку когерентности.

Например, предположим, что наша платформа тестирования имеет возможность создать отчет о результатах теста и сохранить его в файле. Доступ к этой функции осуществляется с помощью параметра --generatereport, а имя файла указывается с --reportfilename myfile.rep. В этом сценарии, если пользователь предоставляет только параметр --generatereport без указания имени файла, проверка должна завершиться ошибкой, так как отчет не может быть создан без имени файла. Возможной реализацией для приведенного выше примера может быть:

public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions)
{
    bool generateReportEnabled = commandLineOptions.IsOptionSet(GenerateReportOption);
    bool reportFileName = commandLineOptions.TryGetOptionArgumentList(ReportFilenameOption, out string[]? _);

    return (generateReportEnabled || reportFileName) && !(generateReportEnabled && reportFileName)
        ? ValidationResult.InvalidTask("Both `--generatereport` and `--reportfilename` need to be provided simultaneously.")
        : ValidationResult.ValidTask;
}

Обратите внимание, что метод ValidateCommandLineOptionsAsync предоставляет службу ICommandLineOptions, которая используется для получения сведений о аргументах, проанализированных самой платформой.

Расширения ITestSessionLifetimeHandler

ITestSessionLifeTimeHandler — это расширение , которое позволяет выполнять код до начала тестового сеанса и после его завершения.

Чтобы зарегистрировать пользовательскую настройку ITestSessionLifeTimeHandler, используйте следующий API:

var builder = await TestApplication.CreateBuilderAsync(args);

// ...

builder.TestHost.AddTestSessionLifetimeHandle(
    static serviceProvider => new CustomTestSessionLifeTimeHandler());

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

Важный

Последовательность регистрации является важной, так как API вызываются в том порядке, в который они были зарегистрированы.

Интерфейс ITestSessionLifeTimeHandler включает следующие методы:

public interface ITestSessionLifetimeHandler : ITestHostExtension
{
    Task OnTestSessionStartingAsync(
        SessionUid sessionUid,
        CancellationToken cancellationToken);

    Task OnTestSessionFinishingAsync(
        SessionUid sessionUid,
        CancellationToken cancellationToken);
}

public readonly struct SessionUid(string value)
{
    public string Value { get; } = value;
}

public interface ITestHostExtension : IExtension
{
}

ITestSessionLifetimeHandler — это тип ITestHostExtension, который служит основой для всех расширений тестового узла. Как и все остальные точки расширения, он также наследует от IExtension. Таким образом, как и любое другое расширение, можно включить или отключить его с помощью API IExtension.IsEnabledAsync.

Рассмотрим следующие сведения для этого API:

OnTestSessionStartingAsync: этот метод вызывается до начала тестового сеанса и получает объект SessionUid, который предоставляет непрозрачный идентификатор текущего тестового сеанса.

OnTestSessionFinishingAsync: этот метод вызывается после завершения тестового сеанса, гарантируя, что платформа тестирования завершена выполнение всех тестов и сообщила все соответствующие данные платформе. Как правило, в этом методе расширение использует IMessageBus для передачи пользовательских ресурсов или данных в общую шину платформы. Этот метод также может сигнализировать любому пользовательскому внепроцессному расширению , что тестовый сеанс завершился.

Наконец, оба API принимают CancellationToken, который расширение должно учитывать.

Если расширение требует интенсивной инициализации и необходимо использовать шаблон async/await, можно обратиться к Async extension initialization and cleanup. Если вам нужно обмениваться состоянием между точками расширения, вы можете обратиться к разделу CompositeExtensionFactory<T>.

Расширения ITestApplicationLifecycleCallbacks

ITestApplicationLifecycleCallbacks — это расширение внутрипроцессного, которое позволяет выполнять код до всего, как иметь доступ к первой строке гипотетического основного узла тестового узла.

Чтобы зарегистрировать кастомный ITestApplicationLifecycleCallbacks, используйте следующий интерфейс API.

var builder = await TestApplication.CreateBuilderAsync(args);

// ...

builder.TestHost.AddTestApplicationLifecycleCallbacks(
    static serviceProvider
    => new CustomTestApplicationLifecycleCallbacks());

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

Важный

Последовательность регистрации является важной, так как API вызываются в том порядке, в который они были зарегистрированы.

Интерфейс ITestApplicationLifecycleCallbacks включает следующие методы:

public interface ITestApplicationLifecycleCallbacks : ITestHostExtension
{
    Task BeforeRunAsync(CancellationToken cancellationToken);

    Task AfterRunAsync(
        int exitCode,
        CancellationToken cancellation);
}

public interface ITestHostExtension : IExtension
{
}

ITestApplicationLifecycleCallbacks — это тип ITestHostExtension, который служит основой для всех расширений тестового узла. Как и все остальные точки расширения, он также наследует от IExtension. Таким образом, как и любое другое расширение, можно включить или отключить его с помощью API IExtension.IsEnabledAsync.

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

Например, встроенная функция дампа зависания состоит из внутрипроцессных и внепроцессных расширений, и этот метод используется для обмена информацией с внепроцессного компонента расширения.

AfterRunAsync: этот метод является последним вызовом перед выходом из int ITestApplication.RunAsync() и предоставляет exit code. Он предназначен исключительно для задач очистки и уведомления любого соответствующего расширения , работающего вне процесса, что тестовый узел сейчас будет завершаться.

Наконец, оба API принимают CancellationToken, который расширение должно учитывать.

Расширения IDataConsumer

IDataConsumer — это расширение в процессе, которое может подписываться на и получать сведения IData, которые передаются в IMessageBus платформой тестирования и ее расширениями.

Эта точка расширения имеет решающее значение, так как разработчики могут собирать и обрабатывать все сведения, созданные во время тестового сеанса.

Чтобы зарегистрировать кастомный IDataConsumer, используйте следующий интерфейс API.

var builder = await TestApplication.CreateBuilderAsync(args);

// ...

builder.TestHost.AddDataConsumer(
    static serviceProvider => new CustomDataConsumer());

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

Важный

Последовательность регистрации является важной, так как API вызываются в том порядке, в который они были зарегистрированы.

Интерфейс IDataConsumer включает следующие методы:

public interface IDataConsumer : ITestHostExtension
{
    Type[] DataTypesConsumed { get; }

    Task ConsumeAsync(
        IDataProducer dataProducer,
        IData value,
        CancellationToken cancellationToken);
}

public interface IData
{
    string DisplayName { get; }
    string? Description { get; }
}

IDataConsumer — это тип ITestHostExtension, который служит основой для всех расширений тестового узла. Как и все остальные точки расширения, он также наследует от IExtension. Таким образом, как и любое другое расширение, можно включить или отключить его с помощью API IExtension.IsEnabledAsync.

DataTypesConsumed: это свойство возвращает список Type, которые это расширение планирует потреблять. Он соответствует IDataProducer.DataTypesProduced. В частности, IDataConsumer может подписаться на несколько типов, исходящих из разных экземпляров IDataProducer без каких-либо проблем.

ConsumeAsync: Этот метод активируется всякий раз, когда данные типа, на который подписан текущий потребитель, помещаются в IMessageBus. Он получает IDataProducer для предоставления сведений о производителе полезной нагрузки, а также о самой полезной нагрузке IData. Как видно, IData — это универсальный интерфейс заполнителя, содержащий общие информативные данные. Возможность принудительного отправки различных типов IData подразумевает, что потребитель должен переключаться на самом типе, чтобы привести его к правильному типу и получить доступ к определенной информации.

Пример реализации клиента, который хочет подробно разработать TestNodeUpdateMessage, созданный с помощью тестовой платформы , может быть:

internal class CustomDataConsumer : IDataConsumer, IOutputDeviceDataProducer
{
    public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) };
    ...
    public Task ConsumeAsync(
        IDataProducer dataProducer,
        IData value,
        CancellationToken cancellationToken)
    {
        var testNodeUpdateMessage = (TestNodeUpdateMessage)value;

        switch (testNodeUpdateMessage.TestNode.Properties.Single<TestNodeStateProperty>())
        {
            case InProgressTestNodeStateProperty _:
                {
                    ...
                    break;
                }
            case PassedTestNodeStateProperty _:
                {
                    ...
                    break;
                }
            case FailedTestNodeStateProperty failedTestNodeStateProperty:
                {
                    ...
                    break;
                }
            case SkippedTestNodeStateProperty _:
                {
                    ...
                    break;
                }
            ...
        }

        return Task.CompletedTask;
    }
...
}

Наконец, API принимает CancellationToken, которое расширение должно учитывать.

Важный

Важно обрабатывать полезную нагрузку непосредственно в методе ConsumeAsync. IMessageBus может управлять как синхронной, так и асинхронной обработкой, координируя выполнение с фреймворком тестирования. Хотя использование полностью асинхронно и не блокирует IMessageBus.Push на момент написания, это подробности реализации, которые могут измениться в будущем из-за новых требований. Однако платформа гарантирует, что этот метод всегда вызывается ровно один раз, устраняя необходимость сложной синхронизации, а также помогает управлять масштабируемостью потребителей.

Предупреждение

При использовании IDataConsumer в сочетании с ITestHostProcessLifetimeHandler в составной точке расширенияважно игнорировать все данные, полученные после выполнения ITestSessionLifetimeHandler.OnTestSessionFinishingAsync. OnTestSessionFinishingAsync является последней возможностью обработки накопленных данных и передачи новых сведений в IMessageBus, поэтому любые данные, потребляемые за пределами этой точки, не будут пригодны для использования расширением.

Если расширение требует интенсивной инициализации и необходимо использовать шаблон async/await, можно обратиться к Async extension initialization and cleanup. Если вам нужно обмениваться состоянием между точками расширения, вы можете обратиться к разделу CompositeExtensionFactory<T>.

Расширения ITestHostEnvironmentVariableProvider

ITestHostEnvironmentVariableProvider — это внепроцессное расширение , которое позволяет устанавливать пользовательские переменные среды для тестового узла. Использование этой точки расширения гарантирует, что платформа тестирования инициирует новый узел с соответствующими переменными среды, как описано в разделе архитектуры.

Чтобы зарегистрировать пользовательскую настройку ITestHostEnvironmentVariableProvider, используйте следующий API:

var builder = await TestApplication.CreateBuilderAsync(args);

// ...

builder.TestHostControllers.AddEnvironmentVariableProvider(
    static serviceProvider => new CustomEnvironmentVariableForTestHost());

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

Важный

Последовательность регистрации является важной, так как API вызываются в том порядке, в который они были зарегистрированы.

Интерфейс ITestHostEnvironmentVariableProvider включает следующие методы и типы:

public interface ITestHostEnvironmentVariableProvider : ITestHostControllersExtension, IExtension
{
    Task UpdateAsync(IEnvironmentVariables environmentVariables);

    Task<ValidationResult> ValidateTestHostEnvironmentVariablesAsync(
        IReadOnlyEnvironmentVariables environmentVariables);
}

public interface IEnvironmentVariables : IReadOnlyEnvironmentVariables
{
    void SetVariable(EnvironmentVariable environmentVariable);
    void RemoveVariable(string variable);
}

public interface IReadOnlyEnvironmentVariables
{
    bool TryGetVariable(
        string variable,
        [NotNullWhen(true)] out OwnedEnvironmentVariable? environmentVariable);
}

public sealed class OwnedEnvironmentVariable : EnvironmentVariable
{
    public IExtension Owner { get; }

    public OwnedEnvironmentVariable(
        IExtension owner,
        string variable,
        string? value,
        bool isSecret,
        bool isLocked);
}

public class EnvironmentVariable
{
    public string Variable { get; }
    public string? Value { get; }
    public bool IsSecret { get; }
    public bool IsLocked { get; }
}

ITestHostEnvironmentVariableProvider — это тип ITestHostControllersExtension, который служит основой для всех расширений контроллера узла тестирования. Как и все остальные точки расширения, он также наследует от IExtension. Таким образом, как и любое другое расширение, можно включить или отключить его с помощью API IExtension.IsEnabledAsync.

Рассмотрим сведения об этом API:

UpdateAsync. Этот API обновления предоставляет экземпляр объекта IEnvironmentVariables, из которого можно вызвать методы SetVariable или RemoveVariable. При использовании SetVariableнеобходимо передать объект типа EnvironmentVariable, который требует следующих спецификаций:

  • Variable: имя переменной среды.
  • Value: значение переменной среды.
  • IsSecret. Это указывает, содержит ли переменная среды конфиденциальную информацию, которая не должна быть зарегистрирована или доступна через TryGetVariable.
  • IsLocked. Это определяет, могут ли другие расширения ITestHostEnvironmentVariableProvider изменять это значение.

ValidateTestHostEnvironmentVariablesAsync: этот метод вызывается после вызова всех методов UpdateAsync зарегистрированных экземпляров ITestHostEnvironmentVariableProvider. Он позволяет проверить правильную настройку переменных среды. Он принимает объект, реализующий IReadOnlyEnvironmentVariables, который предоставляет метод TryGetVariable для получения определенных сведений об переменной среды с типом объекта OwnedEnvironmentVariable. После проверки вы возвращаете ValidationResult со всеми причинами сбоя.

Заметка

Платформа тестирования по умолчанию реализует и регистрирует SystemEnvironmentVariableProvider. Этот поставщик загружает все текущие переменные среды. В качестве первого зарегистрированного поставщика он выполняется первым, предоставляя доступ к переменным среды по умолчанию для всех остальных расширений пользователей ITestHostEnvironmentVariableProvider.

Если расширение требует интенсивной инициализации и необходимо использовать шаблон async/await, можно обратиться к Async extension initialization and cleanup. Если вам нужно обмениваться состоянием между точками расширения, вы можете обратиться к разделу CompositeExtensionFactory<T>.

Расширения ITestHostProcessLifetimeHandler

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

Чтобы зарегистрировать пользовательскую настройку ITestHostProcessLifetimeHandler, используйте следующий API:

var builder = await TestApplication.CreateBuilderAsync(args);

// ...

builder.TestHostControllers.AddProcessLifetimeHandler(
    static serviceProvider => new CustomMonitorTestHost());

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

Важный

Последовательность регистрации является важной, так как API вызываются в том порядке, в который они были зарегистрированы.

Интерфейс ITestHostProcessLifetimeHandler включает следующие методы:

public interface ITestHostProcessLifetimeHandler : ITestHostControllersExtension
{
    Task BeforeTestHostProcessStartAsync(CancellationToken cancellationToken);

    Task OnTestHostProcessStartedAsync(
        ITestHostProcessInformation testHostProcessInformation,
        CancellationToken cancellation);

    Task OnTestHostProcessExitedAsync(
        ITestHostProcessInformation testHostProcessInformation,
        CancellationToken cancellation);
}

public interface ITestHostProcessInformation
{
    int PID { get; }
    int ExitCode { get; }
    bool HasExitedGracefully { get; }
}

ITestHostProcessLifetimeHandler — это тип ITestHostControllersExtension, который служит основой для всех расширений контроллера узла тестирования. Как и все остальные точки расширения, он также наследует от IExtension. Таким образом, как и любое другое расширение, можно включить или отключить его с помощью API IExtension.IsEnabledAsync.

Рассмотрим следующие сведения для этого API:

BeforeTestHostProcessStartAsync: этот метод вызывается перед тем, как платформа тестирования инициирует тестовые узлы.

OnTestHostProcessStartedAsync: этот метод вызывается сразу после запуска тестового узла. Этот метод предлагает объект, реализующий интерфейс ITestHostProcessInformation, который предоставляет ключевые сведения о результатах процесса узла тестирования.

Важный

Вызов этого метода не останавливает выполнение тестового узла. Если необходимо приостановить его, следует зарегистрировать внутрипроцессное расширение , например ITestApplicationLifecycleCallbacks, и синхронизировать его с внепроцессным расширением .

OnTestHostProcessExitedAsync: этот метод вызывается при завершении выполнения набора тестов. Этот метод предоставляет объект, который соответствует интерфейсу ITestHostProcessInformation, который передает важные сведения о результатах процесса тестового узла.

Интерфейс ITestHostProcessInformation предоставляет следующие сведения:

  • PID: идентификатор процесса тестового узла.
  • ExitCode: код выхода процесса. Это значение доступно только в методе OnTestHostProcessExitedAsync. Попытка доступа к нему в методе OnTestHostProcessStartedAsync приведет к исключению.
  • HasExitedGracefully: логическое значение, указывающее, произошел ли сбой узла тестирования. Если значение true, это означает, что хост теста не завершился корректно.

Порядок выполнения расширений

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

  1. ITestHostEnvironmentVariableProvider.UpdateAsync: внепроцессный
  2. ITestHostEnvironmentVariableProvider.ValidateTestHostEnvironmentVariablesAsync: вне процесса
  3. ITestHostProcessLifetimeHandler.BeforeTestHostProcessStartAsync : внепроцессный
  4. Запуск тестового процесса хоста
  5. ITestHostProcessLifetimeHandler.OnTestHostProcessStartedAsync: внепроцессное событие может переплетаться с действиями внутрипроцессных расширений в зависимости от условий гонки.
  6. ITestApplicationLifecycleCallbacks.BeforeRunAsync: в процессе
  7. ITestSessionLifetimeHandler.OnTestSessionStartingAsync: в процессе
  8. ITestFramework.CreateTestSessionAsync: в процессе
  9. ITestFramework.ExecuteRequestAsync: в процессе этот метод можно вызывать один или несколько раз. На этом этапе платформа тестирования передает сведения IMessageBus, которые можно использовать IDataConsumer.
  10. ITestFramework.CloseTestSessionAsync: в процессе
  11. ITestSessionLifetimeHandler.OnTestSessionFinishingAsync: в процессе
  12. ITestApplicationLifecycleCallbacks.AfterRunAsync: в процессе
  13. Очистка во время выполнения включает вызов метода dispose и IAsyncCleanableExtension во всех точках расширения.
  14. ITestHostProcessLifetimeHandler.OnTestHostProcessExitedAsync: внепроцессный
  15. Очистка вне процесса включает вызов метода dispose и IAsyncCleanableExtension на всех точках расширения.

Помощники расширений

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

Асинхронная инициализация и очистка расширений

Создание тестового фреймворка и расширений с помощью фабрик соответствует стандартному механизму создания объектов .NET, который использует синхронные конструкторы. Если расширение требует интенсивной инициализации (например, доступа к файловой системе или сети), оно не может использовать шаблон async/await в конструкторе, так как конструкторы возвращают пустоту, а не Task.

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

public interface IAsyncInitializableExtension
{
    Task InitializeAsync();
}

public interface IAsyncCleanableExtension
{
    Task CleanupAsync();
}

IAsyncInitializableExtension.InitializeAsync: этот метод должен вызываться после фабрики создания.

IAsyncCleanableExtension.CleanupAsync: этот метод гарантированно вызывается по крайней мере один раз во время завершения сеанса тестирования до DisposeAsync по умолчанию или Dispose.

Важный

Аналогично стандартному методу Dispose, CleanupAsync может вызываться несколько раз. Если метод CleanupAsync объекта вызывается несколько раз, объект должен игнорировать все вызовы после первого. Объект не должен вызывать исключение, если его метод CleanupAsync вызывается несколько раз.

Заметка

По умолчанию платформа тестирования будет вызывать DisposeAsync, если она доступна, или Dispose, если она реализована. Важно отметить, что платформа тестирования не будет вызывать оба метода удаления, но будет определять приоритет асинхронного, если он реализован.

КомпозитнаяФабрикаРасширений<T>

Как описано в разделе расширений, платформа тестирования позволяет реализовать интерфейсы для интеграции пользовательских расширений как в рамках процесса, так и за его пределами.

Каждый интерфейс обращается к определенной функции и в соответствии с .NET проектированием реализует этот интерфейс в определенном объекте. Вы можете зарегистрировать расширение с помощью конкретного API регистрации AddXXX из объекта TestHost или TestHostController из ITestApplicationBuilder, как описано в соответствующих разделах.

Однако, если вам нужно поделиться состоянием между двумя расширениями, факт, что можно реализовать и зарегистрировать разные объекты, которые используют различные интерфейсы, делает эту задачу сложной. Без какой-либо помощи вам потребуется способ передать одно расширение другому, чтобы поделиться информацией, что усложняет проектирование.

Таким образом, платформа тестирования предоставляет изощренный метод для реализации нескольких точек расширения с использованием одного типа, что делает общий доступ к данным простой задачей. Все, что необходимо сделать, — использовать CompositeExtensionFactory<T>, который затем можно зарегистрировать с помощью того же API, что и для реализации одного интерфейса.

Например, рассмотрим тип, реализующий как ITestSessionLifetimeHandler, так и IDataConsumer. Это распространенный сценарий, так как часто требуется собирать сведения из платформы тестирования , а затем при завершении сеанса тестирования вы будете отправлять артефакт с помощью IMessageBus в ITestSessionLifetimeHandler.OnTestSessionFinishingAsync.

Что вам следует сделать — это нормально реализовать интерфейсы:

internal class CustomExtension : ITestSessionLifetimeHandler, IDataConsumer, ...
{
   ...
}

После создания CompositeExtensionFactory<CustomExtension> для вашего типа, его можно зарегистрировать с помощью API IDataConsumer и ITestSessionLifetimeHandler, которые поддерживают перегрузку для CompositeExtensionFactory<T>:

var builder = await TestApplication.CreateBuilderAsync(args);

// ...

var factory = new CompositeExtensionFactory<CustomExtension>(serviceProvider => new CustomExtension());

builder.TestHost.AddTestSessionLifetimeHandle(factory);
builder.TestHost.AddDataConsumer(factory);

Конструктор фабрики использует IServiceProvider для доступа к службам, предоставляемым платформой тестирования.

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

Важно отметить, что из-за поддержки платформы тестирования как для внутрипроцессных, так и для внепроцессных расширений нельзя произвольно объединить любую точку расширения. Создание и использование расширений зависят от типа узла, то есть можно группировать только расширения, работающие внутри процесса, (TestHost) и расширения, работающие вне процесса, (TestHostController).

Возможны следующие сочетания:

  • Для ITestApplicationBuilder.TestHostможно объединить IDataConsumer и ITestSessionLifetimeHandler.
  • Для ITestApplicationBuilder.TestHostControllersможно объединить ITestHostEnvironmentVariableProvider и ITestHostProcessLifetimeHandler.