Compartilhar via


Geração de teste

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

Veja a seguir 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 geralmente consegue determinar automaticamente valores relevantes de argumentos para testes de unidade mais gerais e parametrizados, que fornecem a sequência de chamadas de método e expressões.

Geradores de teste

O IntelliTest gera casos de teste selecionando uma sequência de métodos da implementação em teste a serem executados e gerando entradas para os métodos enquanto verifica as declarações sobre os dados derivados.

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

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

Teste de unidade parametrizado

Testes de Unidade Parametrizados (PUTs) são testes que levam 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. É simples assim? Sim - a partir daí, o IntelliTest tentará gerar o conjunto (mínimo) de entradas que abrangem totalmente o código acessível do teste.

Os PUTs são definidos usando o atributo personalizado PexMethod de maneira 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);
}

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

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

Teste de unidade parametrizado genérico

Os testes de unidade 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)
    { ... }
}

Permitindo exceções

O IntelliTest fornece vários atributos de validação para ajudar a triagem de 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 possa vê-los. 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")]

Suposições e declarações

Os usuários podem usar suposições e declarações para expressar pré-condições (suposições) e pós-condições (declarações) sobre seus testes. Quando o IntelliTest gera um conjunto de valores de parâmetro 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 declaraçõ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 com suporte. No entanto, a maioria das estruturas não fornece uma classe Assume . Nesse caso, o IntelliTest fornece a classe PexAssume . Se você não quiser usar uma estrutura de teste existente, o IntelliTest também terá 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 terá êxito.

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, supondo que suas pré-condições eram inicialmente válidas.

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

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

Falhas de teste

Quando um caso de teste gerado falha?

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

  2. Se o teste gerar um PexAssumeFailedException, ele terá êxito. No entanto, ele geralmente é filtrado, a menos que TestEmissionFilter esteja definido como Todos

  3. Se o teste violar uma asserçã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 nenhum dos acima produzir uma decisão, um teste terá êxito se e somente se ele não gerar uma exceção. As violações de declaração são tratadas da mesma forma que as exceções.

Configuração e desmonte

Como parte da integração com estruturas de teste, o IntelliTest dá suporte à detecção e à execução de métodos de instalação e demolição.

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?

Poste suas ideias e solicitações de recursos na Comunidade de Desenvolvedores.