Afirmações do MSTest

Use as Assert classes do Microsoft.VisualStudio.TestTools.UnitTesting namespace para verificar funcionalidade específica. Um método de teste exercita o código na sua aplicação, mas só reporta a correção quando inclui declarações Assert.

Visão geral

O MSTest fornece três classes de asserção:

Class Propósito
Assert Afirmações de propósito geral para valores, tipos e exceções.
StringAssert Asserções específicas de cadeias de caracteres para padrões, substrings e comparações.
CollectionAssert Afirmações de coleção para comparar e validar coleções.

Importante

Para código novo, usa sempre a Assert classe. As StringAssert classes e CollectionAssert provavelmente serão descontinuadas numa versão futura. São mantidos principalmente para compatibilidade retroativa, mas não são recomendados porque dividir asserções em três tipos prejudica a descoberta.

Todos os métodos de asserção aceitam um parâmetro de mensagem opcional que aparece quando a asserção falha, ajudando a identificar a causa:

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

A Assert classe

Use a classe Assert para verificar se o código em teste se comporta conforme o esperado.

Observação

A partir do MSTest 4.0, todas Assert as APIs capturam a expressão do argumento e incluem-na nas mensagens de falha. Este suporte proporciona diagnósticos mais ricos sem necessidade de um parâmetro manual message .

Métodos comuns de asserção

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

O método Assert.That

A partir do MSTest 4.0, Assert.That avalia qualquer expressão booleana e produz uma mensagem clara de falha. Para diagnósticos mais detalhados, Assert.That usa [CallerArgumentExpression] para capturar automaticamente o texto da expressão.

Assert.That(order.Total > 0);

APIs disponíveis

Observação

A partir do MSTest 3.8, as asserções de cobrança incluem Assert.Contains, Assert.DoesNotContain, Assert.HasCount, Assert.IsEmpty, Assert.IsNotEmpty, e Assert.ContainsSingle.

A partir do MSTest 3.10, as asserções de comparação incluem Assert.IsInRange, Assert.IsGreaterThan, Assert.IsGreaterThanOrEqualTo, Assert.IsLessThan, Assert.IsLessThanOrEqualTo, Assert.IsPositive, , e Assert.IsNegative.

A partir do MSTest 3.10, as asserções de correspondência de strings incluem Assert.StartsWith, Assert.EndsWith, Assert.MatchesRegex, Assert.DoesNotStartWith, Assert.DoesNotEndWith, e Assert.DoesNotMatchRegex.

A partir do MSTest 4.1, Assert.IsExactInstanceOfType e Assert.IsNotExactInstanceOfType requerem uma correspondência exata de tipo. Ao contrário de Assert.IsInstanceOfType, estes métodos não correspondem aos tipos derivados.

Novas afirmações de recolha e equivalência no MSTest 4.3

Observação

Os seguintes métodos de afirmação foram introduzidos no MSTest 4.3.0.

  • Assert.AreSequenceEqual / Assert.AreNotSequenceEqual — comparação de sequências elemento a elemento. Passa SequenceOrder.InAnyOrder para ignorar a ordem dos elementos.
  • Assert.AreEquivalent / Assert.AreNotEquivalent — comparação estrutural profunda de dois objetos ou coleções.
  • Assert.ContainsAll / Assert.DoesNotContainAll — afirmar que uma coleção contém (ou não contém) todos os elementos esperados.
  • Assert.AreAllNotNull — afirmam que todo elemento de uma coleção é não-null.
  • Assert.AreAllDistinct — afirmam que todos os elementos de uma coleção são distintos.
  • Assert.AreAllOfType — afirmam que todo elemento de uma coleção é de um tipo esperado.

Ao comparar coleções, prefere estes métodos a Assert.AreEqual, que compara referências em vez de elementos.

O MSTest 4.3 acrescenta também:

  • Assert.AddValueFormatter para personalizar a forma como os valores são renderizados nas mensagens de falha de asserção.
  • Span<T> e Memory<T> sobrecarrega para Assert.HasCount.
  • Mensagens estruturadas de falha de asserção para Assert.IsTrue, Assert.IsFalse, Assert.IsNull, e Assert.IsNotNull que incluem a expressão avaliada.
  • Sobrecargas de mensagem com cadeias interpoladas para os métodos assíncronos Assert.ThrowsAsync/Assert.ThrowsExactlyAsync, e rejeição de delegados que devolvem ValueTask<TResult> e que, de outro modo, não seriam aguardados.

Asserções suaves com Assert.Scope()

Importante

Assert.Scope() é uma API experimental. Ao usá-lo, gera o diagnóstico MSTESTEXP, que pode suprimir (por exemplo, com #pragma warning disable MSTESTEXP ou no ficheiro .editorconfig do seu projeto) para indicar que a forma e o comportamento da API podem mudar em futuras versões.

Por defeito, cada assertiva lança um AssertFailedException assim que falha, o que interrompe imediatamente o teste. Assert.Scope() Introduz asserções suaves: enquanto um âmbito está ativo, as falhas de asserção são recolhidas em vez de lançadas, pelo que a execução continua e pode ver todas as falhas no âmbito ao mesmo tempo. Quando o âmbito é descartado, as falhas recolhidas são reportadas em conjunto:

[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 o âmbito é descartado:

  • Se exatamente uma falha foi recolhida, o original AssertFailedException é descartado.
  • Se tiverem sido recolhidas várias falhas, é lançada uma única AssertFailedException que envolve todas num AggregateException.

As pós-condições não são impostas dentro de um âmbito

Como uma asserção falhada já não inclui um escopo, o código que corre depois dela não pode confiar que a asserção tenha tido sucesso. Isto aplica-se a todas as postcondições, incluindo nulidade e estreitamento de tipos:

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 uma afirmação falhada levasse a uma NullReferenceException (ou qualquer outra exceção) numa linha posterior dentro do âmbito, essa exceção secundária é um sintoma da falha já recolhida, e não um bug separado. A falha de asserção original continua a ser comunicada quando o contexto é eliminado.

As afirmações que retornam valor retornam null/default em caso de falha dentro de um âmbito

Algumas asserções retornam um valor no sucesso — por exemplo, Throws e ThrowsExactly retornam a exceção capturada, e ContainsSingle devolvem o elemento correspondente. Quando uma destas asserções falha dentro de um escopo, a falha é recolhida e o método retorna null/default em vez de lançar:

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
}

Não confie no valor devolvido por uma asserção flexível dentro de um escopo. Se precisares do valor devolvido (como a exceção detetada), chama a asserção fora do âmbito ou reestrutura o teste para que nada dependa do valor de retorno até depois de o âmbito ser eliminado.

Assert.Fail e Assert.Inconclusive lançar sempre

Fail e Inconclusive nunca são moles. Eles lançam sempre imediatamente, mesmo dentro de um telescópio, porque expressam um resultado de teste incondicional. Use um deles quando uma condição for crítica e o resto do teste não puder continuar de forma significativa sem ele.

Os âmbitos aninhados não são suportados

Não é possível aninhar chamadas Assert.Scope(). Apenas um escopo de asserção pode estar ativo em simultâneo.

A StringAssert classe

Use a StringAssert classe para comparar e examinar cadeias de caracteres.

Advertência

É provável que a StringAssert classe seja descontinuada numa versão futura. É mantido apenas para compatibilidade retroativa e não é recomendado para código novo. Todos os métodos StringAssert têm equivalentes na classe Assert, que oferece maior facilidade de descoberta. Para migrar usos existentes, veja o analisador MSTEST0046.

As APIs disponíveis são:

A CollectionAssert classe

Use a CollectionAssert classe para comparar coleções de objetos ou para verificar o estado de uma coleção.

Advertência

É provável que a CollectionAssert classe seja descontinuada numa versão futura. É mantido principalmente para compatibilidade retroativa e não é recomendado para código novo. Quando existe um método equivalente em Assert (como Assert.Contains, Assert.DoesNotContain, ou Assert.HasCount), use Assert para melhor descoberta.

As APIs disponíveis são:

Crie asserções personalizadas com Assert.That

Os métodos de afirmação incorporados não cobrem todos os cenários. Para estender a infraestrutura de asserções com verificações próprias, o MSTest expõe a propriedade singleton Assert.That como ponto de extensibilidade. Pode adicionar asserções personalizadas como métodos de extensão em C# no tipo de instância Assert, e os autores das chamadas invocam-nas com a sintaxe familiar Assert.That.MyAssertion(...).

Para melhor descoberta, organize as asserções a nível de projeto numa classe estática dedicada. As asserções personalizadas acedidas através de Assert.That aparecem juntamente com os métodos incorporados no IntelliSense, para que os utilizadores não tenham de se lembrar de um tipo auxiliar separado.

Criar uma asserção personalizada

Adicione um método de extensão que vise o Assert tipo e lança AssertFailedException quando a condição falha:

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

Use uma asserção personalizada

Depois de importar o namespace que contém os seus métodos de extensão, chame a sua asserção personalizada através de Assert.That.

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

Ganchos de extensão em StringAssert e CollectionAssert

As propriedades StringAssert.That e CollectionAssert.That disponibilizam o mesmo padrão de singleton para retrocompatibilidade. Para novas asserções personalizadas, deve ter sempre como alvo Assert.That. Caso contrário, os seus auxiliares herdam os mesmos problemas de capacidade de descoberta que as classes antigas e terão de ser migrados se StringAssert e CollectionAssert forem descontinuados.

Assert.That propriedade versus Assert.That(...) método

Observação

Não confunda a Assert.Thatpropriedade singleton — usada como gancho de extensibilidade — com o Assert.That(() => condition)método introduzido no MSTest 3.8. Esta última aceita uma expressão booleana e produz mensagens detalhadas de falha ao analisar a árvore de expressões (por exemplo, Assert.That(() => order.Total > 0)). As duas APIs partilham um nome mas servem propósitos diferentes.

Melhores práticas

  • Use afirmações específicas: Prefira AreEqual em vez de IsTrue(a == b) para obter melhores mensagens de falha.

  • Inclua mensagens descritivas: Ajude a identificar falhas rapidamente com mensagens claras de afirmação.

  • Teste uma coisa de cada vez: Cada método de teste deve verificar um único comportamento.

  • Utilização Throws/ThrowsExactly para exceções: No MSTest v3.8+, preferem Assert.Throws, Assert.ThrowsExactly, e os seus equivalentes assíncronos (ThrowsAsync, ThrowsExactlyAsync) em vez do ExpectedException atributo.

  • Prefere Assert em vez de StringAssert/CollectionAssert: Para maior facilidade de localização e consistência, utiliza a classe Assert. As StringAssert classes e CollectionAssert provavelmente serão descontinuadas numa versão futura.

  • Expandir Assert.That com asserções personalizadas: Para uma facilidade de localização consistente, adicione asserções personalizadas como métodos de extensão no Assert e invoque-as através de Assert.That. Não uses StringAssert.That nem CollectionAssert.That como destino em código novo.

Os seguintes analisadores ajudam a garantir a utilização correta das asserções:

  • MSTEST0006 - Evita ExpectedException atributos, usa Assert.Throws métodos em vez disso.
  • MSTEST0017 - Os argumentos de afirmação devem ser apresentados na ordem correta.
  • MSTEST0023 - Não negues afirmações booleanas.
  • MSTEST0025 - Prefere Assert.Fail a condições sempre falsas.
  • MSTEST0026 - Os argumentos de asserção devem evitar o acesso condicional.
  • MSTEST0032 - Revisar condições de asserção sempre verdadeiras.
  • MSTEST0037 - Use métodos adequados de afirmação.
  • MSTEST0038 - Evite Assert.AreSame com os tipos de valor.
  • MSTEST0039 - Use métodos mais Assert.Throws recentes.
  • MSTEST0040 - Evite usar assertos em contexto void assíncrono.
  • MSTEST0046 - Use Assert em vez de StringAssert.
  • MSTEST0051 - Assert.Throws deve conter uma única instrução.
  • MSTEST0053 - Evite Assert parâmetros de formato.
  • MSTEST0058 - Evite assertivas em blocos catch.

Consulte também