Compartir a través de


Generación de pruebas

En las pruebas unitarias tradicionales, una prueba consta de varias cosas:

A continuación se muestra una estructura de prueba de ejemplo:

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

    // method sequence
    a.Add(5);

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

IntelliTest a menudo puede determinar automáticamente los valores de argumento pertinentes para pruebas unitarias con parámetros más generales, que proporcionan la secuencia de llamadas y aserciones de método.

Generadores de pruebas

IntelliTest genera casos de prueba seleccionando una secuencia de métodos de la implementación en prueba que se va a ejecutar y, a continuación, generando entradas para los métodos al comprobar las aserciones sobre los datos derivados.

Una prueba unitaria parametrizada declara directamente en su cuerpo una secuencia de llamadas de método.

Cuando IntelliTest necesita construir objetos, las llamadas a constructores y métodos de fábrica se agregarán automáticamente a la secuencia según sea necesario.

Pruebas unitarias con parámetros

Las pruebas unitarias parametrizadas (PUT) son pruebas que toman parámetros. A diferencia de las pruebas unitarias tradicionales, que suelen ser métodos cerrados, las PUT toman cualquier conjunto de parámetros. ¿Es tan sencillo? Sí: desde allí, IntelliTest intentará generar el conjunto (mínimo) de entradas que cubren completamente el código accesible desde la prueba.

Las PUT se definen mediante el atributo personalizado PexMethod de forma similar a MSTest (o NUnit, xUnit). Las PUT son métodos de instancia agrupados lógicamente en clases etiquetadas con PexClass. En el ejemplo siguiente se muestra un PUT simple almacenado en la clase MyPexTest :

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

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

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

donde ReplaceFirstChar es un método que reemplaza el primer carácter de una cadena:

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 de esta prueba, IntelliTest puede generar automáticamente entradas para un PUT que cubre muchas rutas de acceso de ejecución del código probado. Cada entrada que cubre una ruta de ejecución diferente se convierte en "prueba unitaria" al ser "serializada".

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

Pruebas unitarias parametrizadas genéricas

Las pruebas unitarias con parámetros pueden ser métodos genéricos. En este caso, el usuario debe especificar los tipos usados para crear instancias del método mediante PexGenericArguments.

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

Permitir excepciones

IntelliTest proporciona numerosos atributos de validación para ayudar a evaluar las excepciones en excepciones esperadas y excepciones inesperadas.

Las excepciones esperadas generan casos de prueba negativos con la anotación adecuada, como ExpectedException(typeof(xxx)), mientras que las excepciones inesperadas generan casos de prueba con errores.

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

Los validadores son:

Probar tipos internos

IntelliTest puede "probar" tipos internos, siempre y cuando pueda verlos. Para que IntelliTest vea los tipos, el siguiente atributo se agrega al proyecto de prueba o producto mediante los asistentes de IntelliTest de Visual Studio:

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

Suposiciones y aserciones

Los usuarios pueden usar suposiciones y aserciones para expresar condiciones previas (suposiciones) y condiciones posteriores (aserciones) sobre sus pruebas. Cuando IntelliTest genera un conjunto de valores de parámetro y "explora" el código, podría infringir una suposición de la prueba. Cuando esto sucede, no generará una prueba para esa ruta de acceso, pero la omitirá silenciosamente.

Las aserciones son un concepto conocido en marcos de pruebas unitarias normales, por lo que IntelliTest ya "entiende" las clases Assert integradas proporcionadas por cada marco de pruebas compatible. Sin embargo, la mayoría de los marcos no proporcionan una clase Assume . En ese caso, IntelliTest proporciona la clase PexAssume . Si no desea usar un marco de pruebas existente, IntelliTest también tiene la clase PexAssert .

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

    ...
}

En concreto, la suposición de no nullness se puede codificar como un atributo personalizado:

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

Precondición

Una condición previa de un método expresa las condiciones en las que el método se realizará correctamente.

Normalmente, la precondición se aplica comprobando los parámetros y el estado del objeto, y lanzando una ArgumentException o InvalidOperationException si se infringe.

En IntelliTest, una condición previa de una prueba unitaria parametrizada se expresa con PexAssume.

Condición posterior

Una condición posterior de un método expresa las condiciones que deben contener durante y después de la ejecución del método, suponiendo que sus condiciones previas eran inicialmente válidas.

Normalmente, la condición posterior se aplica mediante llamadas a los métodos Assert.

Con IntelliTest, una condición posterior de una prueba unitaria parametrizada se expresa con PexAssert.

Errores de pruebas

¿Cuándo se produce un error en un caso de prueba generado?

  1. Si no finaliza dentro de los límites de ruta de acceso configurados, se considera un error a menos que se establezca la opción TestExcludePathBoundsExceeded

  2. Si la prueba lanza una excepción PexAssumeFailedException, se considera exitosa. Sin embargo, normalmente se filtra a menos que TestEmissionFilter esté establecido en All

  3. Si la prueba infringe una aserción; por ejemplo, al lanzar una excepción de infracción de aserción de un framework de pruebas unitarias, falla.

Si ninguno de los anteriores produce una decisión, una prueba tiene éxito si y solo si no lanza una excepción. Las infracciones de aserción se tratan de la misma manera que las excepciones.

Configuración y desmontaje

Como parte de la integración con marcos de pruebas, IntelliTest admite la detección y ejecución de métodos de instalación y desmontaje.

Ejemplo

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

Lectura adicional

¿Tiene comentarios?

Publique sus ideas y solicitudes de características en la Comunidad de desarrolladores.