Asserzioni MSTest

Usare le classi Assert dello spazio dei nomi Microsoft.VisualStudio.TestTools.UnitTesting per verificare funzionalità specifiche. Un metodo di test esegue il codice nell'applicazione ma segnala la correttezza solo quando si includono Assert istruzioni.

Informazioni generali

MSTest offre tre classi di asserzione:

Class Scopo
Assert Asserzioni per utilizzo generico per valori, tipi ed eccezioni.
StringAssert Asserzioni specifiche della stringa per modelli, sottostringhe e confronti.
CollectionAssert Asserzioni di raccolta per il confronto e la convalida delle raccolte.

Importante

Per il nuovo codice, usare sempre la Assert classe . Le classi StringAssert e CollectionAssert saranno probabilmente deprecate in una prossima versione. Vengono mantenuti principalmente per la compatibilità con le versioni precedenti, ma non sono consigliati perché la suddivisione delle asserzioni in tre tipi fa male all'individuabilità.

Tutti i metodi di asserzione accettano un parametro di messaggio facoltativo che viene visualizzato quando l'asserzione ha esito negativo, consentendo di identificare la causa:

Assert.AreEqual(expected, actual, "Values should match after processing");

Classe Assert

Usare la classe Assert per verificare che il codice sottoposto a test si comporti come previsto.

Metodi di asserzione comuni

[TestMethod]
public async Task AssertExamples()
{
    // Equality
    Assert.AreEqual(5, calculator.Add(2, 3));
    Assert.AreNotEqual(0, result);

    // Reference equality
    Assert.AreSame(expected, actual);
    Assert.AreNotSame(obj1, obj2);

    // Boolean conditions
    Assert.IsTrue(result > 0);
    Assert.IsFalse(string.IsNullOrEmpty(name));

    // Null checks
    Assert.IsNull(optionalValue);
    Assert.IsNotNull(requiredValue);

    // Type checks
    Assert.IsInstanceOfType<IDisposable>(obj);
    Assert.IsNotInstanceOfType<string>(obj);

    // Exception testing (MSTest v3.8+)
    Assert.ThrowsExactly<ArgumentNullException>(() => service.Process(null!));
    await Assert.ThrowsExactlyAsync<InvalidOperationException>(
        async () => await service.ProcessAsync());
}

API disponibili

Asserzioni soft con Assert.Scope()

Importante

Assert.Scope() è un'API sperimentale. Usandolo viene generato il messaggio diagnostico MSTESTEXP, che è possibile sopprimere (ad esempio, con #pragma warning disable MSTESTEXP o nel file .editorconfig del progetto) per riconoscere esplicitamente che la struttura e il comportamento dell'API possono cambiare nelle versioni future.

Per impostazione predefinita, ogni asserzione genera un'eccezione AssertFailedException non appena ha esito negativo, che termina immediatamente il test. Assert.Scope() introduce asserzioni soft: mentre un ambito è attivo, gli errori di asserzione vengono raccolti anziché generati, quindi l'esecuzione continua ed è possibile visualizzare ogni errore nell'ambito contemporaneamente. Quando lo scope viene rilasciato, gli errori raccolti vengono segnalati tutti insieme:

[TestMethod]
public void ValidatePerson()
{
    using (Assert.Scope())
    {
        Assert.AreEqual("Jane", person.FirstName); // failure collected, execution continues
        Assert.AreEqual("Doe", person.LastName);   // failure collected, execution continues
        Assert.IsTrue(person.IsActive);            // failure collected, execution continues
    }
    // On Dispose, all collected failures are reported together.
}

Quando l'ambito viene eliminato:

  • Se è stata raccolta esattamente un'eccezione, viene generata quella originale AssertFailedException.
  • Se sono stati raccolti più errori, viene generata un'unica AssertFailedException che li racchiude tutti in un AggregateException.

Le postcondizioni non vengono applicate all'interno di un ambito

Poiché un'asserzione con errori non viene più generata all'interno di un ambito, il codice eseguito dopo non può basarsi sull'asserzione che ha avuto esito positivo. Questo vale per ogni postcondizione, comprese la nullabilità e il restringimento del tipo:

using (Assert.Scope())
{
    Assert.IsNotNull(item);
    // 'item' might still be null here: the failure was collected, not thrown.
    Assert.AreEqual("expected", item.Value);
    // 'item.Value' might not equal "expected" either.
}

Se un'asserzione non riuscita portasse a un NullReferenceException (o a qualsiasi altra eccezione) in una riga successiva nell'ambito, tale eccezione secondaria è un sintomo del fallimento già registrato, non un bug separato. L'errore di asserzione originale viene comunque segnalato quando l'ambito viene eliminato.

Le asserzioni che restituiscono un valore restituiscono null/default in caso di errore all'interno di un ambito

Alcune asserzioni restituiscono un valore in caso di esito positivo, Throws ad esempio e ThrowsExactly restituiscono l'eccezione rilevata e ContainsSingle restituisce l'elemento corrispondente. Quando una di queste asserzioni ha esito negativo all'interno di un ambito, l'errore viene raccolto e il metodo restituisce null/default anziché generare:

using (Assert.Scope())
{
    // No exception is thrown by the lambda, so the assertion fails. The failure is
    // collected and 'ex' is null. Accessing 'ex' below throws NullReferenceException.
    InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => { });
    _ = ex.Message; // NullReferenceException—don't use the return value in a scope
}

Non fare affidamento sul valore restituito da un'asserzione soft all'interno di un ambito. Se è necessario il valore restituito ,ad esempio l'eccezione intercettata, chiamare l'asserzione all'esterno dell'ambito o ristrutturare il test in modo che nulla dipenda dal valore restituito fino a quando non viene eliminato l'ambito.

Assert.Fail e Assert.Inconclusive sempre lanciare

Fail e Inconclusive non sono mai morbidi. Vengono sempre sollevati immediatamente, anche all'interno di un ambito, poiché indicano un esito di test incondizionato. Usarne uno quando una condizione è critica e il resto del test non può continuare in modo significativo senza di esso.

Gli ambiti annidati non sono supportati

Non è possibile annidare le chiamate Assert.Scope(). Un solo ambito di asserzione può essere attivo alla volta.

Classe StringAssert

Usare la classe StringAssert per confrontare ed esaminare le stringhe.

Avvertimento

È probabile che la StringAssert classe sia deprecata in una versione futura. Viene mantenuto solo per la compatibilità con le versioni precedenti e non è consigliato per il nuovo codice. Tutti i StringAssert metodi hanno equivalenti nella Assert classe , che offre una migliore individuabilità. Per eseguire la migrazione degli utilizzi esistenti, vedere analizzatore MSTEST0046.

Le API disponibili sono:

Classe CollectionAssert

Usare la classe CollectionAssert per confrontare raccolte di oggetti o verificare lo stato di una raccolta.

Avvertimento

È probabile che la CollectionAssert classe sia deprecata in una versione futura. Viene gestito principalmente per la compatibilità con le versioni precedenti e non è consigliato per il nuovo codice. Quando esiste un metodo equivalente in Assert (ad esempio Assert.Contains, Assert.DoesNotContaino Assert.HasCount), usare Assert per una migliore individuabilità.

Le API disponibili sono:

Creare asserzioni personalizzate con Assert.That

I metodi di asserzione predefiniti non coprono tutti gli scenari. Per estendere l'infrastruttura delle asserzioni con verifiche personalizzate, MSTest espone la proprietà singleton Assert.That come punto di estensione. Si aggiungono asserzioni personalizzate come metodi di estensione C# nel Assert tipo di istanza e i chiamanti li richiamano con la sintassi familiare Assert.That.MyAssertion(...) .

Per una migliore individuabilità, organizzare asserzioni a livello di progetto in una classe statica dedicata. Le asserzioni personalizzate accessibili tramite Assert.That vengono visualizzate insieme ai metodi predefiniti in IntelliSense, quindi gli utenti non devono ricordare un tipo di supporto separato.

Creare un'asserzione personalizzata

Aggiungere un metodo di estensione per il tipo Assert che solleva AssertFailedException quando la condizione non è soddisfatta:

using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

public static class CustomAssertExtensions
{
    public static void IsPrime(this Assert assert, int value)
    {
        if (value < 2 || Enumerable.Range(2, (int)Math.Sqrt(value) - 1).Any(i => value % i == 0))
        {
            throw new AssertFailedException($"Assert.That.IsPrime failed. Value <{value}> is not a prime number.");
        }
    }
}

Usare un'asserzione personalizzata

Dopo aver importato lo spazio dei nomi che contiene i metodi di estensione, chiama l'asserzione personalizzata tramite Assert.That:

[TestMethod]
public void Compute_ReturnsPrime()
{
    int result = _calculator.NextPrime(10);
    Assert.That.IsPrime(result);
}

Hook di estensione su StringAssert e CollectionAssert

Le proprietà StringAssert.That e CollectionAssert.That espongono lo stesso pattern singleton per garantire la compatibilità con le versioni precedenti. Per le nuove asserzioni personalizzate, specificare sempre come destinazione Assert.That. In caso contrario, i helper erediteranno gli stessi problemi di individuabilità delle classi legacy e dovranno essere migrati se StringAssert e CollectionAssert vengono deprecati.

Assert.That proprietà rispetto a Assert.That(...) metodo

Annotazioni

Non confondere la Assert.Thatproprietà singleton, usata come hook di estendibilità, con il Assert.That(() => condition)metodo aggiunto in MSTest 3.8. Quest'ultimo accetta un'espressione booleana e produce messaggi di errore dettagliati analizzando l'albero delle espressioni , ad esempio Assert.That(() => order.Total > 0). Le due API condividono un nome ma servono scopi diversi.

Procedure consigliate

  • Usare asserzioni specifiche: preferire AreEqualIsTrue(a == b) per messaggi di errore migliori.

  • Includere messaggi descrittivi: consente di identificare rapidamente gli errori con messaggi di asserzione chiari.

  • Testare una cosa alla volta: ogni metodo di test deve verificare un singolo comportamento.

  • Utilizzare Throws/ThrowsExactly per le eccezioni: in MSTest v3.8+, preferire Assert.Throws, Assert.ThrowsExactlye le relative controparti asincrone (ThrowsAsync, ThrowsExactlyAsync) sull'attributo ExpectedException .

  • Preferire Assert a StringAssert/CollectionAssert: per una migliore rintracciabilità e coerenza, usare la classe Assert. Le classi StringAssert e CollectionAssert saranno probabilmente deprecate in una prossima versione.

  • Estendere Assert.That per le asserzioni personalizzate: per un'individuabilità coerente, aggiungere asserzioni personalizzate come metodi di estensione su Assert e richiamarle tramite Assert.That. Non specificare come destinazione StringAssert.That o CollectionAssert.That nel nuovo codice.

Gli analizzatori seguenti consentono di garantire un uso appropriato delle asserzioni:

  • MSTEST0006 - evitare ExpectedException l'attributo, usare i metodi Assert.Throws invece.
  • MSTEST0017 : gli argomenti di asserzione devono essere passati nell'ordine corretto.
  • MSTEST0023 : non negare le asserzioni booleane.
  • MSTEST0025 - Preferire Assert.Fail anziché condizioni sempre false.
  • MSTEST0026 : gli argomenti di asserzione devono evitare l'accesso condizionale.
  • MSTEST0032 - Esaminare le condizioni di asserzione sempre vere.
  • MSTEST0037 : usare metodi di asserzione appropriati.
  • MSTEST0038 : evitare Assert.AreSame con i tipi valore.
  • MSTEST0039 : usare metodi più recenti Assert.Throws .
  • MSTEST0040 : evitare di usare asserzioni nel contesto async void.
  • MSTEST0046 : usare Assert anziché StringAssert.
  • - Assert.Throws MSTEST0051 deve contenere una singola istruzione.
  • MSTEST0053 : evitare Assert parametri di formato.
  • MSTEST0058 : evitare asserzioni nei blocchi catch.

Vedere anche