Creare uno unit test basato sui dati

È possibile usare il framework di unit test Microsoft (MSTest) per il codice gestito per configurare un metodo di unit test per recuperare i valori da un'origine dati. Il metodo viene eseguito in successione per ogni riga nell'origine dati, rendendo più semplice testare una un'ampia varietà di input con un singolo metodo.

Uno unit test basato sui dati può usare uno dei tipi seguenti:

  • dati inline con l'attributo DataRow
  • dati dei membri che usano l'attributo DynamicData
  • da un provider di origine noto usando l'attributo DataSource

Metodo sottoposto a test

Come esempio, si supponga di avere:

  1. Una soluzione denominata MyBank che accetta ed elabora transazioni per diversi tipi di account.

  2. Un progetto in MyBank denominato BankDb, che gestisce le transazioni per gli account.

  3. Una classe denominata Maths nel progetto BankDb, che esegue le funzioni matematiche per garantire che tutte le transazioni siano vantaggiose per la banca.

  4. Un progetto di unit test denominato BankDbTests, per testare il comportamento del componente BankDb.

  5. Una classe di unit test denominata MathsTests, per verificare il comportamento della classe Maths.

Verrà testato un metodo in Maths che somma due numeri interi con un ciclo:

public int AddIntegers(int first, int second)
{
    int sum = first;
    for (int i = 0; i < second; i++)
    {
        sum += 1;
    }

    return sum;
}

Test del metodo

Test basato sui dati inline

Per i test inline, MSTest usa DataRow per specificare i valori usati dal test basato sui dati. Il test in questo esempio viene eseguito successivamente per ogni riga di dati.

[TestMethod]
[DataRow(1, 1, 2)]
[DataRow(2, 2, 4)]
[DataRow(3, 3, 6)]
[DataRow(0, 0, 1)] // The test run with this row fails
public void AddIntegers_FromDataRowTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Test basato sui dati dei membri

MSTest usa DynamicData l'attributo per specificare il nome, il tipo (proprietà, il metodo predefinito) e la definizione del tipo (per impostazione predefinita viene usato il tipo corrente) del membro che fornirà i dati usati dal test basato sui dati.

public static IEnumerable<object[]> AdditionData
{
    get
    {
        return new[]
        { 
            new object[] { 1, 1, 2 },
            new object[] { 2, 2, 4 },
            new object[] { 3, 3, 6 },
            new object[] { 0, 0, 1 }, // The test run with this row fails
        };
    }
}

[TestMethod]
[DynamicData(nameof(AdditionData))]
public void AddIntegers_FromDynamicDataTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

È anche possibile eseguire l'override del nome visualizzato generato predefinito, usando la DynamicDataDisplayName proprietà dell'attributo DynamicData . La firma del metodo del nome visualizzato deve essere public static string e accettare due parametri, il primo di tipo MethodInfo e il secondo di tipo object[].

public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data)
{
    return string.Format("DynamicDataTestMethod {0} with {1} parameters", methodInfo.Name, data.Length);
}

[DynamicData(nameof(AdditionData), DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))]

Test basato sui dati del provider di origine

La creazione di uno unit test basato sull'origine dati prevede i passaggi seguenti:

  1. Creare un'origine dati che contiene i valori usati nel metodo di test. L'origine dati può essere di qualsiasi tipo registrato nel computer che esegue il test.

  2. Aggiungere una proprietà pubblica TestContext di tipo TestContext alla classe di test.

  3. Creare un metodo di unit test

  4. Aggiungere un DataSourceAttribute attributo.

  5. Usare la proprietà DataRow dell'indicizzatore per recuperare i valori da usare in un test.

Creare un'origine dati

Per testare il metodo AddIntegers, creiamo un'origine dati che specifica un intervallo di valori per i parametri e la somma restituita prevista. Nell'esempio verrà creato un database Sql Compact denominato MathsData e una tabella denominata AddIntegersData che contiene i nomi di colonna e i valori seguenti

FirstNumber SecondNumber Sum
0 1 1
1 1 2
2 -3 -1

Aggiungere un oggetto TestContext alla classe di test

Il framework unit test crea un oggetto TestContext per archiviare le informazioni sull'origine dati per un test basato sui dati. Il framework imposta quindi l'oggetto come valore della proprietà TestContext creata.

public TestContext TestContext { get; set; }

Nel metodo di test si accede ai dati tramite la proprietà dell'indicizzatore DataRow di TestContext.

Nota

.NET core non supporta l'attributo DataSource. Se si tenta di accedere ai dati di test in questo modo in un progetto di unit test .NET Core, UWP o WinUI, verrà visualizzato un errore simile a "'TestContext' non contiene una definizione per 'DataRow' e non è possibile trovare un metodo di estensione accessibile "DataRow" che accetta un primo argomento di tipo 'TestContext' (manca una direttiva using o un riferimento ad assembly?)".

Scrivere il metodo di test

Il metodo di test per AddIntegers è piuttosto semplice. Per ogni riga nell'origine dati, chiamare AddIntegers con i valori della colonna FirstNumber e SecondNumber come parametri e verificare il valore restituito rispetto al valore della colonna Sum:

[TestMethod]
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")]
public void AddIntegers_FromDataSourceTest()
{
    var target = new Maths();

    // Access the data
    int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
    int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
    int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Specificare DataSourceAttribute

L'attributo DataSource specifica la stringa di connessione per l'origine dati e il nome della tabella usata nel metodo di test. Le informazioni esatte nella stringa di connessione variano a seconda del tipo di origine dati in uso. In questo esempio è stato usato un database SqlServerCe.

[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0;Data Source=C:\Data\MathsData.sdf", "AddIntegersData")]

Attenzione

Il stringa di connessione può contenere dati sensibili, ad esempio una password. Il stringa di connessione viene archiviato in testo normale nel codice sorgente e nell'assembly compilato. Limitare l'accesso al codice sorgente e all'assembly per proteggere queste informazioni riservate.

L'attributo DataSource dispone di tre costruttori.

[DataSource(dataSourceSettingName)]

Un costruttore con un solo parametro usa le informazioni di connessione archiviate nel file app.config per la soluzione. Il nome dell'elemento XML nel file di configurazione che specifica le informazioni di connessione è dataSourceSettingsName.

L'uso di un file app.config consente di modificare il percorso dell'origine dati senza apportare modifiche allo unit test. Per informazioni su come creare e usare un file app.config, vedere Procedura dettagliata: Uso di un file di configurazione per definire un'origine dati

[DataSource(connectionString, tableName)]

Il costruttore DataSource con due parametri specifica la stringa di connessione per l'origine dati e il nome della tabella che contiene i dati per il metodo di test.

Le stringhe di connessione variano a seconda del tipo di origine dati, ma devono contenere un elemento Provider che specifica il nome invariante del provider di dati.

[DataSource(
    dataProvider,
    connectionString,
    tableName,
    dataAccessMethod
    )]

Usare TestContext.DataRow per l'accesso ai dati

Per accedere ai dati nella tabella AddIntegersData, usare l'indicizzatore TestContext.DataRow. DataRow è un oggetto DataRow, pertanto i valori delle colonne vengono recuperati in base all'indice o ai nomi delle colonne. Poiché i valori vengono restituiti come oggetti, convertirli nel tipo appropriato:

int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);

Eseguire il test e visualizzare i risultati

Al termine della scrittura di un metodo di test, compilare il progetto di test. Il metodo di test viene visualizzato in Esplora test nel gruppo Test non eseguiti. Quando si eseguono, si scrivono e si rieseguono i test, Esplora test visualizza i risultati nei gruppi Test non superati, Test superati e Test non eseguiti. È possibile scegliere Esegui tutto per eseguire tutti i test oppure scegliere Esegui per selezionare un sottoinsieme di test da eseguire.

La barra dei risultati dei test nella parte superiore di Esplora test viene animata durante l'esecuzione dei test. Al termine del test, la barra diventa verde se tutti i test sono stati superati oppure rossa se almeno uno dei test ha avuto esito negativo. Nel riquadro dei dettagli nella parte inferiore della finestra Esplora test appare un riepilogo dell'esecuzione dei test. Selezionare un test per visualizzarne i dettagli nel riquadro inferiore.

Nota

Viene visualizzato un risultato per ogni riga di dati e anche un risultato di riepilogo. Se il test è stato superato in ogni riga di dati, l'esecuzione di riepilogo visualizza Superato. Se il test non è stato superato in una riga di dati, l'esecuzione di riepilogo visualizza Non superato.

Se è stato eseguito uno dei metodi o AddIntegers_FromDataSourceTest AddIntegers_FromDynamicDataTest nell'esempioAddIntegers_FromDataRowTest, la barra dei risultati diventa rossa e il metodo di test viene spostato nei test non superati. Un test basato sui dati non riesce se uno dei metodi di cui si esegue l'iterazione dall'origine dati ha esito negativo. Quando si sceglie un test basato sui dati non riuscito nella finestra Esplora test, il riquadro dei dettagli visualizza i risultati di ogni iterazione identificata dall'indice delle righe di dati. Nel nostro esempio, risulta che l'algoritmo AddIntegers non gestisce correttamente i valori negativi.

Dopo avere corretto il metodo sottoposto a test ed eseguito nuovamente il test, la barra dei risultati diventa verde e il metodo di test viene spostato nel gruppo Test superati.