다음을 통해 공유


Microsoft.Testing.Platform Services

테스트 플랫폼은 테스트 프레임워크와 확장 지점 모두에 중요한 서비스를 제공합니다. 이러한 서비스는 구성 액세스, 명령줄 인수 구문 분석 및 검색, 로깅 팩터리 가져오기, 로깅 시스템 액세스 등의 일반적인 요구 사항을 충족합니다. IServiceProvider 테스트 플랫폼에 대한 서비스 로케이터 패턴 구현합니다.

IServiceProvider 기본 클래스 라이브러리에서 직접 파생됩니다.

namespace System
{
    public interface IServiceProvider
    {
        object? GetService(Type serviceType);
    }
}

테스트 플랫폼은 잘 알려진 서비스 개체에 액세스하는 편리한 확장 메서드를 제공합니다. 이러한 모든 메서드는 Microsoft.Testing.Platform.Services 네임스페이스 내의 정적 클래스에 저장됩니다.

public static class ServiceProviderExtensions
{
    public static TService GetRequiredService<TService>(
        this IServiceProvider provider)

    public static TService? GetService<TService>(
        this IServiceProvider provider)

    public static IMessageBus GetMessageBus(
        this IServiceProvider serviceProvider)

    public static IConfiguration GetConfiguration(
        this IServiceProvider serviceProvider)

    public static ICommandLineOptions GetCommandLineOptions(
        this IServiceProvider serviceProvider)

    public static ILoggerFactory GetLoggerFactory(
        this IServiceProvider serviceProvider)

    public static IOutputDevice GetOutputDevice(
        this IServiceProvider serviceProvider)

    // ... and more
}

확장 지점에서 노출되는 대부분의 등록 팩터리는 IServiceProvider대한 액세스를 제공합니다. 예를 들어 테스트 프레임워크를 등록하는경우 IServiceProvider 팩터리 메서드에 매개 변수로 전달됩니다.

ITestApplicationBuilder RegisterTestFramework(
    Func<IServiceProvider, ITestFrameworkCapabilities> capabilitiesFactory,
    Func<ITestFrameworkCapabilities, IServiceProvider, ITestFramework> adapterFactory);

앞의 코드에서 capabilitiesFactoryadapterFactory 모두 매개 변수로 IServiceProvider 제공합니다.

IConfiguration 서비스

IConfiguration 인터페이스는 IServiceProvider 사용하여 검색할 수 있으며 테스트 프레임워크 및 확장 지점에 대한 구성 설정에 대한 액세스를 제공합니다. 기본적으로 이러한 구성은 다음에서 로드됩니다.

  • 환경 변수
  • 진입점 어셈블리 근처에 있는 [assemblyName].testingplatformconfig.json JSON 파일입니다.

우선 순위가 유지됩니다. 즉, 환경 변수에 구성이 있으면 JSON 파일이 처리되지 않습니다.

인터페이스는 간단한 키-값 문자열 쌍입니다.

public interface IConfiguration
{
    string? this[string key] { get; }
}

JSON 구성 파일

JSON 파일은 계층 구조를 따릅니다. 자식 속성에 액세스하려면 : 구분 기호를 사용해야 합니다. 예를 들어 다음과 같은 잠재적 테스트 프레임워크에 대한 구성을 고려합니다.

{
  "CustomTestingFramework": {
    "DisableParallelism": true
  }
}

코드 조각은 다음과 같습니다.

IServiceProvider serviceProvider = null; // Get the service provider...

var configuration = serviceProvider.GetConfiguration();

if (bool.TryParse(configuration["CustomTestingFramework:DisableParallelism"], out var value) && value is true)
{
    // ...
}

배열의 경우 다음과 같습니다.

{
  "CustomTestingFramework": {
    "Engine": [
      "ThreadPool",
      "CustomThread"
    ]
  }
}

첫 번째 요소("ThreadPool")에 액세스하는 구문은 다음과 같습니다.

IServiceProvider serviceProvider = null; // Get the service provider...

var configuration = serviceProvider.GetConfiguration();

var fistElement = configuration["CustomTestingFramework:Engine:0"];

환경 변수

: 구분 기호는 모든 플랫폼의 환경 변수 계층 키에서 작동하지 않습니다. __이중 밑줄은 다음과 같습니다.

  • 모든 플랫폼에서 지원됩니다. 예를 들어 : 구분 기호는 Bash에서 지원되지 않지만 __는 지원됩니다.
  • 자동으로 :로 대체됨

예를 들어 환경 변수는 다음과 같이 설정할 수 있습니다(이 예제는 Windows에 적용 가능).

setx CustomTestingFramework__DisableParallelism=True

ITestApplicationBuilder만들 때 환경 변수 구성 원본을 사용하지 않도록 선택할 수 있습니다.

var options = new TestApplicationOptions();

options.Configuration.ConfigurationSources.RegisterEnvironmentVariablesConfigurationSource = false;

var builder = await TestApplication.CreateBuilderAsync(args, options);

ICommandLineOptions 서비스

ICommandLineOptions 서비스는 플랫폼이 구문 분석한 명령줄 옵션에 대한 세부 정보를 가져오는 데 활용됩니다. 사용 가능한 API는 다음과 같습니다.

public interface ICommandLineOptions
{
    bool IsOptionSet(string optionName);

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

ICommandLineOptionsICommandLineOptionsProvider같은 특정 API를 통해 얻을 수 있으며, 또는 확장 메서드 를 통해 serviceProvider.GetCommandLineOptions()에서 인스턴스를 검색할 수 있습니다.

ICommandLineOptions.IsOptionSet(string optionName): 이 메서드를 사용하면 특정 옵션이 지정되었는지 여부를 확인할 수 있습니다. optionName지정할 때 -- 접두사를 생략합니다. 예를 들어 사용자 입력이 --myOption경우 myOption전달하면 됩니다.

ICommandLineOptions.TryGetOptionArgumentList(string optionName, out string[]? arguments): 이 메서드를 사용하면 특정 옵션이 설정되었는지 확인하고, 설정된 경우 해당 값 또는 값을 검색할 수 있습니다(진도가 둘 이상인 경우). 이전 사례와 마찬가지로 optionName-- 접두사 없이 제공해야 합니다.

ILoggerFactory 서비스

테스트 플랫폼에는 로그 파일을 생성하는 통합 로깅 시스템이 함께 제공됩니다. --help 명령을 실행하여 로깅 옵션을 볼 수 있습니다. 선택할 수 있는 옵션은 다음과 같습니다.

--diagnostic                             Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag
--diagnostic-synchronous-write Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution.
--diagnostic-output-directory            Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory.
--diagnostic-file-prefix           Prefix for the log file name that will replace '[log]_.'
--diagnostic-verbosity                   Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical'

코딩의 관점에서 정보를 기록하려면 ILoggerFactory에서 IServiceProvider를 가져와야 합니다. ILoggerFactory API는 다음과 같습니다.

public interface ILoggerFactory
{
    ILogger CreateLogger(string categoryName);
}

public static class LoggerFactoryExtensions
{
    public static ILogger<TCategoryName> CreateLogger<TCategoryName>(this ILoggerFactory factory);
}

로거 팩터리를 사용하면 ILogger API를 사용하여 CreateLogger 개체를 만들 수 있습니다. 범주 이름으로 사용되는 제네릭 인수를 허용하는 편리한 API도 있습니다.

public interface ILogger
{
    Task LogAsync<TState>(
        LogLevel logLevel, 
        TState state, 
        Exception? exception, 
        Func<TState, Exception?, string> formatter);

    void Log<TState>(
        LogLevel logLevel,
        TState state, 
        Exception? exception, 
        Func<TState, Exception?, string> formatter);

    bool IsEnabled(LogLevel logLevel);
}

public interface ILogger<out TCategoryName> : ILogger
{
}

public static class LoggingExtensions
{
    public static Task LogCriticalAsync(this ILogger logger, string message);
    public static Task LogDebugAsync(this ILogger logger, string message);
    public static Task LogErrorAsync(this ILogger logger, Exception ex);
    public static Task LogErrorAsync(this ILogger logger, string message, Exception ex);
    public static Task LogErrorAsync(this ILogger logger, string message);
    public static Task LogInformationAsync(this ILogger logger, string message);
    public static Task LogTraceAsync(this ILogger logger, string message);
    public static Task LogWarningAsync(this ILogger logger, string message);
    public static void LogCritical(this ILogger logger, string message);
    public static void LogDebug(this ILogger logger, string message);
    public static void LogError(this ILogger logger, Exception ex);
    public static void LogError(this ILogger logger, string message, Exception ex);
    public static void LogError(this ILogger logger, string message);
    public static void LogInformation(this ILogger logger, string message);
    public static void LogTrace(this ILogger logger, string message);
    public static void LogWarning(this ILogger logger, string message);
}

ILogger만든 ILoggerFactory 개체는 다양한 수준에서 정보를 로깅하기 위한 API를 제공합니다. 이러한 로깅 수준은 다음과 같습니다.

public enum LogLevel
{
    Trace,
    Debug,
    Information,
    Warning,
    Error,
    Critical,
    None,
}

다음은 로깅 API를 사용하는 방법의 예입니다.

...
IServiceProvider provider = null; // Get the service provider...

var factory = provider.GetLoggerFactory();

var logger = factory.CreateLogger<TestingFramework>();

// ...

if (logger.IsEnabled(LogLevel.Information))
{
    await logger.LogInformationAsync(
        $"Executing request of type '{context.Request}'");
}

// ...

불필요한 할당을 방지하려면 API를 사용하여 ILogger.IsEnabled(LogLevel)로 활성화되어 있는지 확인해야 합니다.

IMessageBus 서비스

메시지 버스 서비스는 테스트 프레임워크와 확장 간의 정보 교환을 용이하게 하는 중앙 메커니즘입니다.

테스트 플랫폼의 메시지 버스는 게시-구독 패턴사용합니다.

공유 버스의 가장 중요한 구조는 다음과 같습니다.

메시지 버스와 다양한 확장의 상호 작용을 나타내는 그림입니다.

확장 및 테스트 프레임워크를 포함하는 다이어그램에 설명된 것처럼 버스에 정보를 푸시하거나 버스에서 정보를 사용하는 두 가지 잠재적인 작업이 있습니다.

IMessageBus을 만족하며 작업을 버스로 푸시했고, API는 다음과 같습니다.

public interface IMessageBus
{
    Task PublishAsync(
        IDataProducer dataProducer, 
        IData data);
}

public interface IDataProducer : IExtension
{
    Type[] DataTypesProduced { get; }
}

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

매개 변수에 대한 다음 세부 정보를 고려합니다.

  • IDataProducer: IDataProducer은 제공할 수 있는 정보의 Type를 메시지 버스에 전달하고, IExtension 기본 인터페이스로부터의 상속을 통해 소유권을 확립합니다. 이는 데이터를 메시지 버스에 무차별적으로 푸시할 수 없음을 의미합니다. 미리 생성된 데이터 형식을 선언해야 합니다. 예기치 않은 데이터를 푸시하면 예외가 트리거됩니다.

  • IData: 이 인터페이스는 이름 및 설명과 같은 설명적인 세부 정보만 제공해야 하는 자리 표시자 역할을 합니다. 인터페이스는 의도적인 데이터의 특성에 대해 많이 공개하지 않습니다. 이는 테스트 프레임워크와 확장이 모든 형식의 데이터를 버스에 푸시할 수 있으며 등록된 확장 또는 테스트 프레임워크 자체에서 이 데이터를 사용할 수 있음을 의미합니다.

이 접근법은 정보 교환 프로세스의 진화를 촉진하여 확장이 새 데이터에 익숙하지 않을 때 비호환적인 변경을 방지합니다. 서로 다른 버전의 확장과 테스트 프레임워크가 상호 이해따라 조화롭게 작동할 수 있습니다.

버스의 반대쪽 끝은 특정 유형의 데이터를 구독하여 이를 사용할 수 있는 소비자로 불립니다.

중요하다

항상 await를 사용하여 PublishAsync호출을 기다립니다. 그렇지 않은 경우 테스트 플랫폼 및 확장에서 IData 올바르게 처리되지 않을 수 있으며 이로 인해 미묘한 버그가 발생할 수 있습니다. 대기에서 돌아온 후에만 메시지 버스에서 처리를 위해 IData가 대기 중임을 확신할 수 있습니다. 작업 중인 확장 지점에 관계없이, 확장을 종료하기 전에 모든 PublishAsync 호출을 기다렸는지 확인하십시오. 예를 들어, 당신이 testing framework을 구현하고 있는 경우, 특정 요청에 대한 모든 Complete 호출을 대기할 때까지는 요청에 대해 PublishAsync을 호출해서는 안 됩니다.

IOutputDevice 서비스

테스트 플랫폼은 출력 디바이스아이디어를 캡슐화하여 테스트 프레임워크 및 확장이 현재 사용 중인 디스플레이 시스템에 모든 종류의 데이터를 전송하여 정보를 수 있도록 합니다.

출력 디바이스 가장 일반적인 예는 콘솔 출력입니다.

메모

테스트 플랫폼은 사용자 지정 출력 디바이스지원하도록 설계되었지만 현재 이 확장 지점을 사용할 수 없습니다.

출력 장치에 데이터를 전송하려면, 먼저 IOutputDevice에서 IServiceProvider를 가져와야 합니다.

API는 다음으로 구성됩니다.

public interface IOutputDevice
{
    Task DisplayAsync(
        IOutputDeviceDataProducer producer, 
        IOutputDeviceData data);
}

public interface IOutputDeviceDataProducer : IExtension
{
}

public interface IOutputDeviceData
{
}

IOutputDeviceDataProducerIExtension을 확장하고 발신자에 대한 정보를 에서출력 디바이스로 제공합니다.

IOutputDeviceData 자리 표시자 인터페이스 역할을 합니다. IOutputDevice 개념은 색이 지정된 텍스트보다 더 복잡한 정보를 수용하는 것입니다. 예를 들어 그래픽으로 나타낼 수 있는 복잡한 개체일 수 있습니다.

테스트 플랫폼은 기본적으로 IOutputDeviceData 개체에 대해 기존의 색이 지정된 텍스트 모델을 제공합니다.

public class TextOutputDeviceData : IOutputDeviceData
{
    public TextOutputDeviceData(string text)
    public string Text { get; }
}

public sealed class FormattedTextOutputDeviceData : TextOutputDeviceData
{
    public FormattedTextOutputDeviceData(string text)
    public IColor? ForegroundColor { get; init; }
    public IColor? BackgroundColor { get; init; }
}

public sealed class SystemConsoleColor : IColor
{
    public ConsoleColor ConsoleColor { get; init; }
}

다음은 활성 출력 디바이스에서 색이 지정된 텍스트를 사용하는 방법의 예입니다.

IServiceProvider provider = null; // Get the service provider...

var outputDevice = provider.GetOutputDevice();

await outputDevice.DisplayAsync(
    this, 
    new FormattedTextOutputDeviceData($"TestingFramework version '{Version}' running tests with parallelism of {_dopValue}")
    {
        ForegroundColor = new SystemConsoleColor
        {
            ConsoleColor = ConsoleColor.Green
        }
    });

색이 지정된 텍스트의 표준 사용 외에도 IOutputDeviceIOutputDeviceData 주요 이점은 출력 디바이스 완전히 독립적이며 사용자에게 알려지지 않는다는 것입니다. 이렇게 하면 복잡한 사용자 인터페이스를 개발할 수 있습니다. 예를 들어 테스트 진행률을 표시하는 실시간 웹 애플리케이션을 구현하는 것이 전적으로 가능합니다.

IPlatformInformation 서비스

이름, 버전, 커밋 해시 및 빌드 날짜와 같은 플랫폼에 대한 정보를 제공합니다.