Microsoft.Testing.Platform (MTP) capacità

Nel contesto di MTP, una funzionalità si riferisce al potenziale per eseguire un'azione specifica o fornire informazioni specifiche. È un mezzo per il framework di test e le estensioni per dichiarare la loro abilità di operare in un certo modo o fornire informazioni specifiche ai richiedenti .

I richiedenti possono essere tutti i componenti coinvolti in una sessione di test, ad esempio la piattaforma, un'estensione o il framework di test stesso.

L'obiettivo principale del sistema di capacità è facilitare la comunicazione efficace tra i componenti coinvolti in una sessione di test, consentendo loro di exchange informazioni e soddisfare accuratamente le rispettive esigenze.

Esempio guidato

Si consideri un esempio ipotetico per dimostrare la necessità di un sistema di funzionalità.

Nota

Questo esempio è puramente per scopi illustrativi e non è attualmente implementato in MTP o in alcun framework di test.

Immagina una situazione in cui si dispone di un'estensione che richiede di eseguire non più di un test alla volta. Inoltre, dopo ogni test, l'estensione deve conoscere l'utilizzo della CPU per tale test specifico.

Per soddisfare lo scenario precedente, è necessario chiedere al framework di test se:

  1. Ha la possibilità di eseguire un solo test alla volta.
  2. Può fornire informazioni sulla quantità di CPU utilizzata da ogni test.

In che modo l'estensione può determinare se il framework di test ha la possibilità di operare in questa modalità e fornire informazioni sull'utilizzo della CPU per una sessione di test? In MTP questa funzionalità è rappresentata da un'implementazione dell'interfaccia Microsoft.Testing.Platform.Capabilities.ICapability:

// Base capabilities contracts

public interface ICapability
{
}

public interface ICapabilities<TCapability>
    where TCapability : ICapability
{
    IReadOnlyCollection<TCapability> Capabilities { get; }
}

// Specific testing framework capabilities

public interface ITestFrameworkCapabilities : ICapabilities<ITestFrameworkCapability>
{
}

public interface ITestFrameworkCapability : ICapability
{
}

Come si può notare, l'interfaccia ICapability è un'interfaccia marcatore perché può rappresentare qualsiasi funzionalitàe l'implementazione effettiva dipenderà dal contesto. Osserverai anche l'ITestFrameworkCapability, che eredita da ICapability per classificare la capacità. La natura generica del sistema di funzionalità consente un raggruppamento pratico in base al contesto. Il ITestFrameworkCapability raggruppa tutte le funzionalità implementate dal framework di test . L'interfaccia ICapabilities<TCapability> rivela il set di di tutte le funzionalità implementate da un'estensione. Analogamente, per la base 1 è disponibile un framework di test specifico del contesto denominato ITestFrameworkCapabilities. Il ITestFrameworkCapabilities viene fornito alla piattaforma durante il processo di registrazione del framework di test.

Per creare una funzionalità che si rivolge allo scenario precedente, è necessario definirla come segue:

public interface IDisableParallelismCapability : ITestFrameworkCapability
{
    bool CanDisableParallelism { get; }
    bool CanProvidePerTestCpuConsumption { get; }
    void Enable();
}

Se il framework di test implementa questa interfaccia, in fase di esecuzione è possibile eseguire una query:

  • Verificare se il framework di test ha la possibilità di disattivare il parallelismo CanDisableParallelism = true.
  • Determinare se il framework di test può fornire i dati di utilizzo della CPU CanProvidePerTestCPUConsumption = true.
  • Richiedere all'adattatore di test di attivare questa modalità richiamando il metodo Enable() prima della sessione di test.

Il frammento di codice ipotetico all'interno dell'estensione potrebbe essere simile al seguente:

IServiceProvider serviceProvider = null; // TODO: Get IServiceProvider.
var capabilities = serviceProvider.GetRequiredService<ITestFrameworkCapabilities>();

// Utilize the `GetCapability` API to search for the specific capability to query.
var capability = capabilities.GetCapability<IDisableParallelismCapability>();
if (capability is null)
{
    // Capability not supported...
}
else
{
    capability.Enable();
    if (capability.CanDisableParallelism)
    {
        // Do something...
    }

    if (capability.CanProvidePerTestCpuConsumption)
    {
        // Do something...
    }
}

Nell'esempio precedente viene illustrato come l'infrastruttura di funzionalità consenta un meccanismo potente per comunicare le capacità tra i componenti coinvolti in una sessione di test. Mentre l'esempio illustra una funzionalità progettata specificamente per il framework di test, qualsiasi componente può esporre e implementare estensioni che ereditano da ICapability.

È evidente che non tutti i dettagli possono essere comunicati tramite un'interfaccia. Considerando l'esempio precedente, cosa dovrebbe aspettarsi l'estensione se il CanProvidePerTestCpuConsumption è supportato? Quali tipi di informazioni personalizzate devono essere trasmesse tramite il IMessageBus dal framework di test? La soluzione è la documentazione della capacità di. È responsabilità della capacità di proprietario progettare, spedire e documentarlo il più chiaramente possibile per assistere gli implementatori che vogliono collaborare efficacemente collaborare con l'estensione che richiede la funzionalità specifica.

Ad esempio, l'estensione del report TRX consente al framework di test di implementare la funzionalità necessaria per generare in modo accurato un report TRX. L'estensione da registrare è inclusa nel https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport pacchetto NuGet, ma la funzionalità da implementare è disponibile nel pacchetto solo contratto.

In conclusione, riepilogiamo gli aspetti principali del sistema di funzionalità:

  • È essenziale per facilitare la comunicazione chiara e stabile tra componenti.
  • Tutte le funzionalità devono ereditare da ICapability o da un'interfaccia che eredita da essa e vengono esposte tramite una raccolta con l'interfaccia ICapabilities.
  • Facilita l'evoluzione delle funzionalità senza causare modifiche di rilievo. Se una determinata funzionalità non è supportata, è possibile eseguire un'azione appropriata.
  • La responsabilità di progettare, spedire e documentare l'utilizzo di una funzionalità spetta al proprietario della funzionalità . MTP può anche possedere una funzionalità nello stesso modo di qualsiasi altra estensione.

Funzionalità del framework

La piattaforma espone un'interfaccia specializzata denominata ITestFrameworkCapability che è la base di tutte le funzionalità esposte per i framework di test. Queste funzionalità vengono fornite quando registrando il framework di test sulla piattaforma.

IBannerMessageOwnerCapability

Una funzionalità facoltativa del framework di test consente al framework di test di fornire il messaggio banner alla piattaforma. Se il messaggio è null o la funzionalità non è presente, la piattaforma usa il messaggio predefinito del banner.

Questa implementazione di funzionalità consente di astrarre le varie condizioni che il framework di test potrebbe dover prendere in considerazione quando si decide se deve essere visualizzato o meno il messaggio banner.

La piattaforma espone il servizio IPlatformInformation per fornire alcune informazioni sulla piattaforma che potrebbero essere utili durante la compilazione del messaggio banner personalizzato.

ITrxReportCapability

Per fornire metadati specifici del framework all'estensione del report TRX, implementare Microsoft.Testing.Extensions.TrxReport.Abstractions.ITrxReportCapability. Fai riferimento al pacchetto Microsoft.Testing.Extensions.TrxReport.Abstractions nel tuo framework. L'applicazione di test richiede anche il pacchetto Microsoft.Testing.Extensions.TrxReport per creare report.

La funzionalità separa il supporto dall'attivazione:

  • Restituisce true da IsSupported quando il framework può fornire metadati TRX.
  • In Enable(), registrare che il reporter TRX ha richiesto i metadati. Il reporter chiama questo metodo all'inizio di una sessione di test quando i report TRX sono abilitati.
  • Dopo l'attivazione, aggiungere le proprietà TRX ai nodi di test che il framework pubblica.

La funzionalità seguente archivia lo stato di attivazione. I progetti di esempio hanno come destinazione .NET 10 e fanno riferimento a MTP 2.5.1:

internal sealed class TrxCapability : ITrxReportCapability
{
    public bool IsSupported => true;
    public bool IsEnabled { get; private set; }

    public void Enable() => IsEnabled = true;
}
Friend NotInheritable Class TrxCapability
    Implements ITrxReportCapability

    Public ReadOnly Property IsSupported As Boolean Implements ITrxReportCapability.IsSupported
        Get
            Return True
        End Get
    End Property

    Public Property IsEnabled As Boolean
        Get
            Return _isEnabled
        End Get
        Private Set(value As Boolean)
            _isEnabled = value
        End Set
    End Property

    Private _isEnabled As Boolean

    Public Sub Enable() Implements ITrxReportCapability.Enable
        IsEnabled = True
    End Sub
End Class

Restituisci la funzionalità da capabilitiesFactory che passi a RegisterTestFramework. Assegnare al framework la stessa istanza della capability in modo che possa osservare Enable(). Per un'applicazione di test composta manualmente, registrare il provider TRX dopo il framework e altre estensioni:

var trxCapability = new TrxCapability();
var builder = await TestApplication.CreateBuilderAsync(args);
builder.RegisterTestFramework(
    _ => new TestFrameworkCapabilities(trxCapability),
    (_, _) => new ReportFramework(trxCapability));
builder.AddTrxReportProvider();
Dim trxCapability As New TrxCapability()
Dim builder = Await TestApplication.CreateBuilderAsync(args)
builder.RegisterTestFramework(
    Function(services) New TestFrameworkCapabilities(trxCapability),
    Function(capabilities, services) New ReportFramework(trxCapability))
builder.AddTrxReportProvider()

Il pacchetto di astrazione fornisce proprietà tipizzate per TestNode.Properties:

Property Purpose
TrxFullyQualifiedTypeNameProperty Fornisce il nome completo della classe di test.
TrxCategoriesProperty Fornisce le categorie del test.
TrxExceptionProperty Fornisce il messaggio di errore e la traccia dello stack.
TrxMessagesProperty Include le voci StandardOutputTrxMessage, StandardErrorTrxMessage e DebugOrTraceTrxMessage.
TrxTestDefinitionName Esegue l'override del nome della definizione di test nel report.
TrxWorkItemsProperty Fornisce gli identificatori degli elementi di lavoro.

Ad esempio, arricchisci un risultato dopo che il framework ha determinato il risultato del test:

if (trxCapability.IsEnabled)
{
    testNode.Properties.Add(
        new TrxFullyQualifiedTypeNameProperty("Contoso.CalculatorTests"));
    testNode.Properties.Add(new TrxCategoriesProperty(["Unit"]));
    testNode.Properties.Add(new TrxMessagesProperty(
        [new StandardOutputTrxMessage($"Captured output for {test.Name}.")]));
    if (failure is not null)
    {
        testNode.Properties.Add(
            new TrxExceptionProperty(failure.Message, failure.StackTrace));
    }
}
If _trxCapability.IsEnabled Then
    testNode.Properties.Add(
        New TrxFullyQualifiedTypeNameProperty("Contoso.CalculatorTests"))
    testNode.Properties.Add(New TrxCategoriesProperty({"Unit"}))
    testNode.Properties.Add(New TrxMessagesProperty(
        {New StandardOutputTrxMessage($"Captured output for {test.Name}.")}))
    If failure IsNot Nothing Then
        testNode.Properties.Add(
            New TrxExceptionProperty(failure.Message, failure.StackTrace))
    End If
End If

Queste proprietà integrano lo stato del nodo di test richiesto; non lo sostituiscono. I messaggi specifici di TRX non sostituiscono nemmeno quelli della piattaforma StandardOutputProperty e StandardErrorProperty.

L'esempio completo contiene un test superato e un test intenzionalmente non superato. Con --report-trx, scrive sia i risultati che i metadati arricchiti in un file TRX, quindi restituisce il codice 2di uscita . Per eseguire solo il test riuscito, aggiungere --filter-uid trx.pass.

Se la funzionalità è assente o IsSupported è false, il reporter TRX avvisa che il framework non supporta l'arricchimento TRX. Il reporter può comunque creare un report dai dati disponibili, ma i dettagli specifici del framework potrebbero non essere presenti. Non annunciare il supporto a meno che il framework non fornisca i metadati corrispondenti.

IGracefulStopTestExecutionCapability

Per supportare --maximum-failed-tests in un framework di test personalizzato, implementare Microsoft.Testing.Platform.Capabilities.TestFramework.IGracefulStopTestExecutionCapability. La funzionalità fa parte del pacchetto Microsoft.Testing.Platform ed è disponibile a partire da MTP 1.5. Gli autori di test che usano un framework che supporta già l'opzione non devono necessariamente implementare la funzionalità.

L'interfaccia definisce Task StopTestExecutionAsync(CancellationToken cancellationToken). Quando il numero di errori raggiunge il valore massimo richiesto, MTP chiama questo metodo. L'implementazione deve indicare al framework di interrompere la pianificazione di nuovi test, mentre consente di completare i test già avviati, eseguire la pulizia e pubblicarne i risultati.

Importante

La funzionalità e l'helper AddMaximumFailedTestsService sono API sperimentali che producono la diagnostica TPEXPdel compilatore. I contratti possono essere modificati o essere rimossi. I progetti di esempio sopprimono TPEXP in modo esplicito.

Quando si implementa la funzionalità, attenersi ai requisiti seguenti:

  • Condividere lo stato di arresto tra la capacità e l'utilità di pianificazione dei test del framework. La semplice restituzione di un task completato non arresta i test.
  • Rendere le richieste di arresto thread-safe e idempotenti. Più componenti della piattaforma possono richiedere un arresto.
  • Tornare dopo aver dato il segnale di stop. Non attendere il completamento dell'intera esecuzione o del bus di messaggi: MTP può chiamare il metodo mentre elabora un risultato del test, quindi tale attesa può causare un deadlock.
  • Mantenere lo stato di arresto controllato separato dall'annullamento della richiesta. Non terminare il processo o annullare la pubblicazione dei risultati per implementare un arresto graduale. Rispettare il token di annullamento del metodo se l'operazione di arresto richiede un lavoro asincrono.
  • Al termine dei test attivi, completare il normale ciclo di vita delle richieste, inclusa la pubblicazione dei risultati, la pulizia e la chiamata a ExecuteRequestContext.Complete().

L'implementazione seguente usa un flag thread-safe. Nel framework verificare IsStopRequested prima di pianificare ogni test e smettere di accettare nuovo lavoro quando il flag è true.

public sealed class GracefulStopCapability : IGracefulStopTestExecutionCapability
{
    private int _stopRequested;

    public bool IsStopRequested => Volatile.Read(ref _stopRequested) != 0;

    public Task StopTestExecutionAsync(CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        Interlocked.Exchange(ref _stopRequested, 1);
        return Task.CompletedTask;
    }
}
Public NotInheritable Class GracefulStopCapability
    Implements IGracefulStopTestExecutionCapability

    Private _stopRequested As Integer

    Public ReadOnly Property IsStopRequested As Boolean
        Get
            Return Volatile.Read(_stopRequested) <> 0
        End Get
    End Property

    Public Function StopTestExecutionAsync(cancellationToken As CancellationToken) As Task _
        Implements IGracefulStopTestExecutionCapability.StopTestExecutionAsync

        cancellationToken.ThrowIfCancellationRequested()
        Interlocked.Exchange(_stopRequested, 1)
        Return Task.CompletedTask
    End Function
End Class

Esporre la funzionalità tramite ITestFrameworkCapabilities.Capabilities. L'esempio di registrazione usa la raccolta di TestFrameworkCapabilities MTP.

Durante la registrazione del framework di test, passare l'istanza di funzionalità registrata al framework. L'helper seguente accetta la factory del framework e un IExtension che contiene l'opzione della riga di comando. Chiama anche AddMaximumFailedTestsService dal namespace Microsoft.Testing.Platform.Helpers per registrare il provider dell'opzione --maximum-failed-tests. Senza tale provider, MTP non riconosce l'opzione.

public static void Register(
    ITestApplicationBuilder builder,
    IExtension owner,
    Func<GracefulStopCapability, ITestFramework> createFramework)
{
    builder.RegisterTestFramework(
        _ => new TestFrameworkCapabilities(new GracefulStopCapability()),
        (capabilities, _) => createFramework(
            capabilities.Capabilities.OfType<GracefulStopCapability>().Single()));
    builder.AddMaximumFailedTestsService(owner);
}
Public Sub Register(
    builder As ITestApplicationBuilder,
    owner As IExtension,
    createFramework As Func(Of GracefulStopCapability, ITestFramework))

    builder.RegisterTestFramework(
        Function(services) New TestFrameworkCapabilities(New GracefulStopCapability()),
        Function(capabilities, services) createFramework(
            capabilities.Capabilities.OfType(Of GracefulStopCapability)().Single()))
    builder.AddMaximumFailedTestsService(owner)
End Sub

La factory di funzionalità crea un nuovo stato di arresto e la factory del framework recupera la stessa istanza. Non creare una seconda capacità disconnessa nel framework. Per i framework che supportano più richieste di esecuzione, mantenere lo stato di arresto limitato all'esecuzione corrispondente. Non cancellare una richiesta di arresto accettata all'avvio dell'esecuzione.

MTP rifiuta --maximum-failed-tests durante la convalida della riga di comando se il provider di opzioni è registrato, ma il framework non espone questa funzionalità. Quando la soglia di errore attiva un arresto, MTP restituisce il codice 13di uscita . Il numero di errori finali può superare la soglia perché l'elaborazione dei risultati è asincrona e i test potrebbero essere già in corso.

Per il contratto di interfaccia, vedere IGracefulStopTestExecutionCapability sorgente.