Partilhar via


Geração de testes

No teste de unidade tradicional, um teste consiste em várias coisas:

Segue-se um exemplo de estrutura de teste:

[Test]
void MyTest() {
    // data
    ArrayList a = new ArrayList();

    // method sequence
    a.Add(5);

    // assertions
    Assert.IsTrue(a.Count==1);
    Assert.AreEqual(a[0], 5);
}

O IntelliTest pode muitas vezes determinar automaticamente valores de argumento relevantes para testes de unidade parametrizados mais gerais, que fornecem a sequência de chamadas de método e asserções.

Geradores de testes

O IntelliTest gera casos de teste selecionando uma sequência de métodos da implementação em teste a ser executada e, em seguida, gerando entradas para os métodos enquanto verifica asserções sobre os dados derivados.

Um teste de unidade parametrizado indica diretamente uma sequência de chamadas de método em seu corpo.

Quando o IntelliTest precisa construir objetos, as chamadas para construtores e métodos de fábrica serão adicionadas automaticamente à sequência, conforme necessário.

Testes unitários parametrizados

Testes de Unidade Parametrizados (PUTs) são testes que usam parâmetros. Ao contrário dos testes de unidade tradicionais, que geralmente são métodos fechados, os PUTs tomam qualquer conjunto de parâmetros. É assim tão simples? Sim - a partir daí, o IntelliTest tentará gerar o conjunto (mínimo) de entradas que cobrem totalmente o código acessível a partir do teste.

PUTs são definidos usando o atributo personalizado PexMethod de forma semelhante ao MSTest (ou NUnit, xUnit). PUTs são métodos de instância agrupados logicamente em classes marcadas com PexClass. O exemplo a seguir mostra um PUT simples armazenado na classe MyPexTest :

[PexMethod]
void ReplaceFirstChar(string target, char c) {

    string result = StringHelper.ReplaceFirstChar(target, c);

    Assert.AreEqual(result[0], c);
}

onde ReplaceFirstChar é um método que substitui o primeiro caractere de uma cadeia de caracteres:

class StringHelper {
    static string ReplaceFirstChar(string target, char c) {
        if (target == null) throw new ArgumentNullException();
        if (target.Length == 0) throw new ArgumentOutOfRangeException();
        return c + target.Substring(1);
    }
}

A partir deste teste, o IntelliTest pode gerar automaticamente entradas para um PUT que abrange muitos caminhos de execução do código testado. Cada entrada que abrange um caminho de execução diferente é "serializada" como um teste de unidade:

[TestMethod, ExpectedException(typeof(ArgumentNullException))]
void ReplaceFirstChar0() {
    this.ReplaceFirstChar(null, 0);
}
...
[TestMethod]
void ReplaceFirstChar10() {
    this.ReplaceFirstChar("a", 'c');
}

Testes unitários parametrizados genéricos

Os testes unitários parametrizados podem ser métodos genéricos. Nesse caso, o usuário deve especificar os tipos usados para instanciar o método usando PexGenericArguments.

[PexClass]
public partial class ListTest {
    [PexMethod]
    [PexGenericArguments(typeof(int))]
    [PexGenericArguments(typeof(object))]
    public void AddItem<T>(List<T> list, T value)
    { ... }
}

Permitir exceções

O IntelliTest fornece vários atributos de validação para ajudar a triar exceções em exceções esperadas e exceções inesperadas.

As exceções esperadas geram casos de teste negativos com a anotação apropriada, como ExpectedException(typeof(xxx)), enquanto exceções inesperadas geram casos de teste com falha.

[PexMethod, PexAllowedException(typeof(ArgumentNullException))]
void SomeTest() {...}

Os validadores são:

Testando tipos internos

O IntelliTest pode "testar" tipos internos, desde que os possa ver. Para que o IntelliTest veja os tipos, o seguinte atributo é adicionado ao seu produto ou projeto de teste pelos assistentes do Visual Studio IntelliTest:

[assembly: InternalsVisibleTo("Microsoft.Pex, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]

Pressupostos e asserções

Os usuários podem usar suposições e asserções para expressar pré-condições (suposições) e pós-condições (asserções) sobre seus testes. Quando o IntelliTest gera um conjunto de valores de parâmetros e "explora" o código, ele pode violar uma suposição do teste. Quando isso acontecer, ele não gerará um teste para esse caminho, mas o ignorará silenciosamente.

As asserções são um conceito bem conhecido em estruturas de teste de unidade regulares, portanto, o IntelliTest já "entende" as classes Assert internas fornecidas por cada estrutura de teste suportada. No entanto, a maioria das estruturas não fornece uma classe Assume . Nesse caso, IntelliTest fornece a classe PexAssume . Se você não quiser usar uma estrutura de teste existente, o IntelliTest também tem a classe PexAssert .

[PexMethod]
public void Test1(object o) {
    // precondition: o should not be null
    PexAssume.IsNotNull(o);

    ...
}

Em particular, a suposição de não nulidade pode ser codificada como um atributo personalizado:

[PexMethod]
public void Test2([PexAssumeNotNull] object o)
// precondition: o should not be null
{
   ...
}

Pré-condição

Uma pré-condição de um método expressa as condições sob as quais o método será bem-sucedido.

Normalmente, a pré-condição é imposta verificando os parâmetros e o estado do objeto e lançando um ArgumentException ou InvalidOperationException se ele for violado.

No IntelliTest, uma pré-condição de um teste de unidade parametrizado é expressa com PexAssume.

Pós-condição

Uma pós-condição de um método expressa as condições que devem ser mantidas durante e após a execução do método, assumindo que suas pré-condições eram inicialmente válidas.

Normalmente, a pós-condição é imposta por chamadas para métodos Assert .

Com o IntelliTest, uma pós-condição de um teste de unidade parametrizado é expressa com PexAssert.

Falhas no teste

Quando um caso de teste gerado falha?

  1. Se ele não terminar dentro dos limites de caminho configurados, será considerado uma falha, a menos que a opção TestExcludePathBoundsExceeded esteja definida

  2. Se o teste lançar um PexAssumeFailedException, o teste terá êxito. No entanto, geralmente é filtrado, a menos que TestEmissionFilter esteja definido como All

  3. Se o teste violar uma afirmação; Por exemplo, ao lançar uma exceção de violação de asserção de uma estrutura de teste de unidade, ela falha

Se nenhuma das opções acima produzir uma decisão, um teste será bem-sucedido se e somente se não lançar uma exceção. As violações de asserção são tratadas da mesma forma que as exceções.

Configuração e desmontagem

Como parte da integração com estruturas de teste, o IntelliTest suporta a deteção e execução de métodos de configuração e desmontagem.

Exemplo

using Microsoft.Pex.Framework;
using NUnit.Framework;

namespace MyTests
{
    [PexClass]
    [TestFixture]
    public partial class MyTestClass
    {
        [SetUp]
        public void Init()
        {
            // monitored
        }

        [PexMethod]
        public void MyTest(int i)
        {
        }

        [TearDown]
        public void Dispose()
        {
            // monitored
        }
    }
}

Leitura adicional

Tem comentários?

Publique suas ideias e solicitações de recursos na Comunidade de desenvolvedores.