Looking for unit-testing Answers? Try Ask4KnowledgeBase
Looking for unit-testing Keywords? Try Ask4Keywords

unit-testingComenzando con la prueba unitaria


Observaciones

La prueba de unidad describe el proceso de prueba de unidades de código individuales aisladas del sistema del que forman parte. Lo que constituye una unidad puede variar de un sistema a otro, desde un método individual hasta un grupo de clases estrechamente relacionadas o un módulo.

La unidad está aislada de sus dependencias utilizando la prueba de dobles cuando es necesario y la configuración en un estado conocido. Su comportamiento en reacción a los estímulos (llamadas a métodos, eventos, datos simulados) se compara con el comportamiento esperado.

Las pruebas unitarias de sistemas completos se pueden realizar utilizando arneses personalizados de prueba, sin embargo, se han escrito muchos marcos de prueba para ayudar a agilizar el proceso y cuidar gran parte de las tareas rutinarias, repetitivas y mundanas. Esto permite a los desarrolladores concentrarse en lo que quieren probar.

Cuando un proyecto tiene suficientes pruebas unitarias, cualquier modificación para agregar una nueva funcionalidad o realizar una refactorización de código se puede hacer fácilmente verificando que todo funciona como antes.

La cobertura del código , normalmente expresada como un porcentaje, es la métrica típica utilizada para mostrar qué parte del código en un sistema está cubierto por las pruebas unitarias; tenga en cuenta que no hay una regla estricta y rápida sobre qué tan alto debe ser, pero generalmente se acepta que cuanto más alto, mejor.

El desarrollo dirigido por pruebas (TDD) es un principio que especifica que un desarrollador debe comenzar a codificar escribiendo una prueba de unidad que falla y solo entonces escribe el código de producción que hace que la prueba pase. Al practicar TDD, se puede decir que las pruebas en sí mismas son el primer consumidor del código que se está creando; por lo tanto, ayudan a auditar y dirigir el diseño del código para que sea tan fácil de usar y tan robusto como sea posible.

Versiones

La prueba de unidad es un concepto que no tiene números de versión.

Una prueba XUnit con parámetros.

using Xunit;

public class SimpleCalculatorTests
{
    [Theory]
    [InlineData(0, 0, 0, true)]
    [InlineData(1, 1, 2, true)]
    [InlineData(1, 1, 3, false)]
    public void Add_PassMultipleParameters_VerifyExpected(
        int inputX, int inputY, int expected, bool isExpectedCorrect)
    {
        // Arrange
        var sut = new SimpleCalculator();

        // Act
        var actual = sut.Add(inputX, inputY);

        // Assert
        if (isExpectedCorrect)
        {
            Assert.Equal(expected, actual);
        }
        else
        {
            Assert.NotEqual(expected, actual);
        }
    }
}

public class SimpleCalculator
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}
 

Una prueba básica de unidad de pitón.

import unittest

def addition(*args):
    """ add two or more summands and return the sum """

    if len(args) < 2:
        raise ValueError, 'at least two summands are needed'
    
    for ii in args: 
        if not isinstance(ii, (int, long, float, complex )):
            raise TypeError

    # use build in function to do the job
    return sum(args) 
 

Ahora la parte de prueba:

class Test_SystemUnderTest(unittest.TestCase):

    def test_addition(self):
        """test addition function"""

        # use only one summand - raise an error 
        with self.assertRaisesRegexp(ValueError, 'at least two summands'):
            addition(1)
        
        # use None - raise an error
        with self.assertRaises(TypeError):
            addition(1, None)
        
        # use ints and floats 
        self.assertEqual(addition(1, 1.), 2)

        # use complex numbers
        self.assertEqual(addition(1, 1., 1+2j), 3+2j)

if __name__ == '__main__':
    unittest.main()
 

Una prueba unitaria básica.

En su forma más simple, una prueba de unidad consta de tres etapas:

  • Preparar el entorno para la prueba.
  • Ejecutar el código a probar.
  • Validar el comportamiento esperado coincide con el comportamiento observado.

Estas tres etapas a menudo se llaman 'Arrange-Act-Assert', o 'Given-When-Then'.

A continuación se muestra un ejemplo en C # que usa el marco NUnit .

[TestFixture]
public CalculatorTest
{
   [Test]
   public void Add_PassSevenAndThree_ExpectTen()
   {
       // Arrange - setup environment
       var systemUnderTest = new Calculator();         

       // Act - Call system under test
       var calculatedSum = systemUnderTest.Add(7, 3);  
       
       // Assert - Validate expected result
       Assert.AreEqual(10, calculatedSum);             
  }
}
 

Donde sea necesario, una cuarta etapa de limpieza opcional se pone en orden.

Una prueba de unidad con un espía (prueba de interacción)

Las pruebas unitarias clásicas prueban el estado , pero puede ser imposible probar correctamente los métodos cuyo comportamiento depende de otras clases a través del estado. Probamos estos métodos a través de pruebas de interacción , que verifican que el sistema bajo prueba llame correctamente a sus colaboradores. Dado que los colaboradores tienen sus propias pruebas de unidad, esto es suficiente, y en realidad es una mejor prueba de la responsabilidad real del método probado. No probamos que este método devuelva un resultado particular a partir de una entrada, sino que llame a su (s) colaborador (es) correctamente.

// Test that squareOfDouble invokes square() with the doubled value

let systemUnderTest = new Calculator()          // Arrange - setup environment
let square = spy()
systemUnderTest.setSquare(square)               //   inject a spy

let actual = systemUnderTest.squareOfDouble(3)  // Act - Call system under test

assert(square.calledWith(6))                    // Assert - Validate expected interaction
 

Una prueba unitaria con dependencia tachonada.

Las buenas pruebas unitarias son independientes, pero el código a menudo tiene dependencias. Usamos varios tipos de pruebas para eliminar las dependencias para las pruebas. Una de las pruebas más sencillas es un trozo. Esta es una función con un valor de retorno codificado en lugar de la dependencia del mundo real.

// Test that oneDayFromNow returns a value 24*60*60 seconds later than current time

let systemUnderTest = new FortuneTeller()       // Arrange - setup environment
systemUnderTest.setNow(() => {return 10000})    //   inject a stub which will 
                                                //   return 10000 as the result

let actual = systemUnderTest.oneDayFromNow()    // Act - Call system under test

assert.equals(actual, 10000 + 24 * 60 * 60)     // Assert - Validate expected result
 

En el código de producción, oneDayFromNow llamaría a Date.now (), pero eso generaría pruebas inconsistentes y poco confiables. Así que aquí lo apagamos.

Prueba simple de Java + JUnit

JUnit es el marco de pruebas líder utilizado para probar código Java.

La clase bajo prueba modela una cuenta bancaria simple, que cobra una multa cuando se sobregira.

public class BankAccount {
    private int balance;

    public BankAccount(int i){
        balance = i;
    }

    public BankAccount(){
        balance = 0;
    }

    public int getBalance(){
        return balance;
    }

    public void deposit(int i){
        balance += i;
    }

    public void withdraw(int i){
        balance -= i;
        if (balance < 0){
            balance -= 10; // penalty if overdrawn
        }
    }
}
 

Esta clase de prueba valida el comportamiento de algunos de los métodos públicos de BankAccount .

import org.junit.Test;
import static org.junit.Assert.*;

// Class that tests
public class BankAccountTest{

    BankAccount acc;

    @Before                        // This will run **before** EACH @Test
    public void setUptestDepositUpdatesBalance(){
        acc = new BankAccount(100);  
    } 

    @After                        // This Will run **after** EACH @Test
    public void tearDown(){
    // clean up code
    }

    @Test
    public void testDeposit(){
       // no need to instantiate a new BankAccount(), @Before does it for us

        acc.deposit(100);

        assertEquals(acc.getBalance(),200); 
    }

    @Test
    public void testWithdrawUpdatesBalance(){    
        acc.withdraw(30);

        assertEquals(acc.getBalance(),70); // pass
    }

    @Test
    public void testWithdrawAppliesPenaltyWhenOverdrawn(){

        acc.withdraw(120);

        assertEquals(acc.getBalance(),-30);
    }
}
 

Prueba de unidad con parámetros usando NUnit y C #

using NUnit.Framework;

namespace MyModuleTests 
{
    [TestFixture]
    public class MyClassTests
    {
        [TestCase(1, "Hello", true)]
        [TestCase(2, "bye", false)]
        public void MyMethod_WhenCalledWithParameters_ReturnsExpected(int param1, string param2, bool expected)
        {
        //Arrange
        var foo = new MyClass(param1);

        //Act
        var result = foo.MyMethod(param2);

        //Assert
        Assert.AreEqual(expected, result);
        }
    }
}