Share via


Criar um teste de unidade controlado por dados

É possível usar a estrutura de teste de unidade da Microsoft (MSTest) para o código gerenciado para configurar um método de teste de unidade para recuperar valores de uma fonte de dados. O método é executado sucessivamente para cada linha na fonte de dados, o que facilita o teste de uma variedade de entradas usando um único método.

Um teste de unidade controlado por dados pode usar qualquer um dos seguintes tipos:

  • dados embutidos usando o atributo DataRow
  • dados membros usando o atributo DynamicData
  • de algum provedor de origem conhecido usando o atributo DataSource

O método em teste

Por exemplo, vamos supor que você tenha:

  1. Uma solução chamada MyBank que aceita e processa transações para diferentes tipos de contas.

  2. Um projeto em MyBank chamado BankDb que gerencia as transações das contas.

  3. Uma classe chamada Maths no projeto BankDb que executa as funções matemáticas para garantir que qualquer transação seja vantajosa para o banco.

  4. Um projeto de teste de unidade chamado BankDbTests para testar o comportamento do componente BankDb.

  5. Uma classe de teste de unidade chamada MathsTests para verificar o comportamento da classe Maths.

Testaremos um método em Maths que adiciona dois números inteiros usando um loop:

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

    return sum;
}

Testar o método de teste

Teste controlado por dados embutido

Para testes embutidos, o MSTest usa DataRow para especificar valores usados pelo teste controlado por dados. Neste exemplo, o teste é executado sucessivamente para cada linha de dados.

[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});
}

Teste membro controlado por dados

O MSTest usa o atributo DynamicData para especificar o nome, o tipo (propriedade, o padrão ou o método) e definir o tipo (por padrão, o tipo atual é usado) do membro que fornecerá os dados usados pelo teste controlado por dados.

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});
}

Também é possível substituir o nome de exibição padrão gerado, usando a propriedade DynamicDataDisplayName do atributo DynamicData. A assinatura do método de nome de exibição deve ser public static string e aceitar dois parâmetros, o primeiro do tipo MethodInfo e o segundo do 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))]

Teste controlado por dados do provedor de origem

A criação de um teste de unidade orientado a fonte de dados envolve as seguintes etapas:

  1. Criar uma fonte de dados que contém os valores a serem usados no método de teste. A fonte de dados pode ser de qualquer tipo que esteja registrado no computador que executa o teste.

  2. Adicione uma propriedade TestContext pública do tipo TestContext à classe de teste.

  3. Crie um método de teste de unidade

  4. Adicione um atributo DataSourceAttribute a ele.

  5. Usar o propriedade do indexador DataRow para recuperar os valores que você pode usar em um teste.

Criar uma fonte de dados

Para testar o método AddIntegers, crie uma fonte de dados que especifica um intervalo de valores para os parâmetros e a soma que você espera que seja retornada. Neste exemplo, criaremos um banco de dados do SQL Compact chamado MathsData e uma tabela chamada AddIntegersData que contém os seguintes nomes de coluna e valores

FirstNumber SecondNumber Somar
0 1 1
1 1 2
2 -3 -1

Adicionar um TestContext à classe de teste

A estrutura de teste de unidade cria um objeto TestContext para armazenar as informações de fonte de dados de um teste orientado a dados. Em seguida, a estrutura define esse objeto como o valor da propriedade TestContext que você criou.

public TestContext TestContext { get; set; }

Em seu método de teste, os dados são acessados por meio da propriedade do indexador DataRow do TestContext.

Observação

O .NET Core não dá suporte ao atributo DataSource. Se tentar acessar os dados de teste dessa forma em um projeto de teste de unidade do .NET Core, UWP ou WinUI, aparecerá um erro semelhante a "TestContext não contém uma definição para 'DataRow' e nenhum 'DataRow' do método de extensão acessível que aceita um primeiro argumento do tipo 'TestContext' pode ser encontrado (alguma diretiva em uso ou uma referência de assembly está ausente?)".

Escrever o método de teste

O método de teste de AddIntegers é bastante simples. Para cada linha na fonte de dados, chame AddIntegers com os valores de coluna FirstNumber e SecondNumber como parâmetros e verifique o valor retornado no valor da coluna 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});
}

Especificar o DataSourceAttribute

O atributo DataSource especifica a cadeia de conexão para a fonte de dados e o nome da tabela a ser usada no método de teste. As informações exatas na cadeia de conexão são diferentes, dependendo do tipo de fonte de dados que você está usando. Neste exemplo, usamos um banco de dados SqlServerCe.

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

O atributo DataSource tem três construtores.

[DataSource(dataSourceSettingName)]

Um construtor com um parâmetro usa informações de conexão armazenadas no arquivo app.config para a solução. O dataSourceSettingsName é o nome do elemento XML no arquivo de configuração que especifica as informações de conexão.

O uso de um arquivo app.config permite que você altere o local da fonte de dados sem fazer alterações no teste de unidade propriamente dito. Para obter informações sobre como criar e usar um arquivo app.config, confira Passo a passo: Usando um arquivo de configuração para definir uma fonte de dados

[DataSource(connectionString, tableName)]

O construtor DataSource com dois parâmetros especifica a cadeia de conexão da fonte de dados e o nome da tabela que contém os dados para o método de teste.

As cadeias de conexão dependem do tipo de fonte de dados, mas elas devem conter um elemento Provedor que especifica o nome invariável do provedor de dados.

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

Usar o TestContext.DataRow para acessar os dados

Para acessar os dados na tabela AddIntegersData, use o indexador TestContext.DataRow. DataRow é um objeto DataRow, para que seja possível recuperar valores de colunas por índice ou nomes de colunas. Como os valores são retornados como objetos, converta-os para o tipo apropriado:

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

Executar o teste e exibir os resultados

Ao terminar de escrever um método de teste, compile o projeto de teste. O método de teste é exibido em Gerenciador de Testes, no grupo Testes Não Executados. Conforme você executa, escreve e executa novamente os testes, o Gerenciador de Testes exibe os resultados em grupos de Testes Reprovados, Testes Aprovados e Testes Não Executados. Você pode escolher Executar Tudo para executar todos os testes ou Executar para escolher um subconjunto de testes a serem executados.

A barra de resultados de teste na parte superior do Gerenciador de Testes é animada enquanto o teste é executado. Ao final da execução de teste, a barra ficará verde se todos os testes passaram ou vermelha se algum dos testes falhou. Um resumo da execução de teste é exibido no painel de detalhes na parte inferior da janela do Gerenciador de Testes. Selecione um teste para exibir seus detalhes no painel inferior.

Observação

Há um resultado para cada linha de dados e também um resumo de resultados. Se o teste foi aprovado em cada linha de dados, o resumo de execução mostra como Aprovado. Se o teste falhou em alguma linha de dados, o resumo de execução mostra como Falha.

Se você executou o método AddIntegers_FromDataRowTest, AddIntegers_FromDynamicDataTestAddIntegers_FromDataSourceTest de nosso exemplo, a barra de resultados fica vermelha e o método de teste é movido para Testes Reprovados. Um teste controlado por dados falha se um dos métodos iterados da fonte de dados falha. Quando você escolhe um teste controlado por dados que foi reprovado na janela Gerenciador de Testes, o painel de detalhes exibe os resultados de cada iteração que é identificada pelo índice de linha de dados. Em nosso exemplo, parece que o algoritmo AddIntegers não manipula valores negativos corretamente.

Quando o método em teste é corrigido e o teste é novamente executado, a barra de resultados ficará verde e o método de teste é movido para o grupo Teste Aprovado.