22 agosto 2008

Pasando de Delphi 7 a RAD Studio 2007 (7)

LAS UNIDADES DE PRUEBAS

Este es otro de los puntos interesantes que incorpora por defecto RAD Studio 2007 y de hecho ya viene incorporado desde las versiones de Delphi 2005 en adelante. Una unidad de prueba (también conocido como test unitario o prueba unitaria) nos permite comprobar automáticamente el comportamiento de una clase aunque luego sigamos haciendo ampliaciones.

Para ello disponemos de la unidad DUnit.pas la cual está basada en JUnit del lenguaje de programación Java. DUnit incorpora funciones para realizar una serie de test a las clases de Delphi para Win32. En Delphi para .NET se utiliza la unidad NUnit.

Vamos a ver un ejemplo de cómo podemos realizar una prueba unitaria a una clase. Supongamos que tenemos una unidad llamada UFactura.pas que contiene una clase para calcular el total de una factura:

unit UFactura;

interface

type
TFactura = class
public
BaseImponible, Iva, Total: Real;

constructor Create( Importe: Real );
procedure Calcular;
end;

implementation

constructor TFactura.Create( Importe: Real );
begin
Iva := 16;
BaseImponible := Importe;
Calcular;
end;

procedure TFactura.Calcular;
begin
Total := BaseImponible + BaseImponible * Iva / 100;
end;

end.

Para realizar una unidad de pruebas lo que hay que hacer realmente es crear un nuevo proyecto destinado exclusivamente para pruebas y añadir la unidad que vamos a testear.

Hay que seguir estos pasos:

1. En el proyecto actual que tengamos abierto mostramos el editor de código fuente (en vez del formulario) para que se pueda ver en la paleta de herramientas (Tool Palette). Abrimos la opción Unit test y hacemos doble clic sobre Test Project:


Aparecerá esta ventana:


Como se puede apreciar en la imagen, el campo Location lo que va a hacer es crear un nuevo proyecto en una carpeta llamada test dentro del la carpeta del proyecto actual. Esta carpeta la va a crear automáticamente.

2. Pulsamos el botón Next y aparecerá esta otra ventana:


En esta ventana se nos da a elegir si queremos que nos resultados los muestre en una ventana de Windows (GUI) en el modo consola (Console).

3. Lo dejamos como está y pulsamos el botón Finish. Después pulsamos el botón guardar.

Lo que hará será crear la nueva carpeta test y dentro meterá el nuevo proyecto creado:

4. Cerramos el proyecto actual y abrimos el proyecto de la carpeta test.

5. Añadimos al proyecto la unidad UFactura.pas que vamos a analizar (Project -> Add to proyect).

6. En la paleta de herramientas (Tool palette) hacemos doble clic sobre Test Case:


Nos aparecerá esta ventana:


7. Seleccionamos la unidad UFactura.pas y veremos abajo los métodos de la clase que queremos probar:


8. Lo dejamos como está y pulsamos el botón Finish.

Creará la siguiente unidad:

unit TestUFactura;
{
Delphi DUnit Test Case
----------------------
This unit contains a skeleton test case class generated by the Test Case Wizard.
Modify the generated code to correctly setup and call the methods from the unit
being tested.
}

interface

uses
TestFramework, UFactura;

type
// Test methods for class TFactura

TestTFactura = class(TTestCase)
strict private
FFactura: TFactura;
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestCalcular;
end;

implementation

procedure TestTFactura.SetUp;
begin
FFactura := TFactura.Create;
end;

procedure TestTFactura.TearDown;
begin
FFactura.Free;
FFactura := nil;
end;

procedure TestTFactura.TestCalcular;
begin
FFactura.Calcular;
// TODO: Validate method results
end;

initialization
// Register any test cases with the test runner
RegisterTest(TestTFactura.Suite);
end.

Esta unidad contiene la clase TestTFactura que hereda de TTestCase la cual incorpora los siguiente métodos:

SetUp: Es el encargado de crear la clase que vamos a probar. En nuestro caso crearía una instancia de la clase TFactura:

procedure TestTFactura.SetUp;
begin
FFactura := TFactura.Create( 100 );
end;

Originalmente me ha creado el constructor sin parámetros, por lo que yo le he añadido manualmente el parámetro 100, que es la base imponible de la factura que voy a probar.

TearDown: Elimina la instancia del objeto creado:

procedure TestTFactura.TearDown;
begin
FFactura.Free;
FFactura := nil;
end;

TestCalcular: Es el encargado de probar el procedimiento Calcular:

procedure TestTFactura.TestCalcular;
begin
FFactura.Calcular;
// TODO: Validate method results
end;

A este último procedimiento le vamos a añadir el comando CheckEquals que evalúa el resultado obtenido con el que esperábamos:

procedure TestTFactura.TestCalcular;
begin
FFactura.Calcular;
CheckEquals( 116, FFactura.Total, 'El Total de la factura es erroneo' ); // test de prueba
end;

El primer parámetro del procedimiento CheckEquals es el número que nosotros esperamos (Total factura = Base Imponible (100) + Iva (16) = 116). El segundo parámetro el valor resultante (la variable Total de la factura). El tercer parámetro es el mensaje que queremos que vea el usuario si hay un error en el resultado).

Si ejecutamos el programa nos aparecerá esta ventana:


Pulsamos el botón Run Selected Test (el botón play de color verde) y si todo ha ido bien nos mostrará todos los puntos de color verde:


Eso significa que el resultado es el esperado. Ahora vamos a estropear el método Calcular a cosa hecha para que nos devuelva un resultado erroneo:

procedure TFactura.Calcular;
begin
Total := BaseImponible + BaseImponible * Iva / 100 + 1;
end;

Si volvemos a ejecutar el test nos aparecerá esto:


En este caso se ha producido un error en el test y si pulsamos abajo su error nos dirá que esperaba 116 y ha devuelto 117.

Esto es muy potente para evitar tener que realizar una batería de pruebas cada vez que tenemos que modificar de nuevo alguna clase.

Ahora supongamos que nos piden que la factura realice un descuento por pronto pago en la base imponible. Modificamos la factura para ello:

type
TFactura = class
public
BaseImponible, Iva, Total, DtoPP: Real;

constructor Create( Importe: Real );
procedure Calcular;
end;

implementation

constructor TFactura.Create( Importe: Real );
begin
Iva := 16;
DtoPP := 0;
BaseImponible := Importe;
Calcular;
end;

procedure TFactura.Calcular;
var
ImporteNeto: Real;
begin
ImporteNeto := BaseImponible - BaseImponible * DtoPP / 100;
Total := ImporteNeto + ImporteNeto * Iva / 100;
end;

Esto es lo que más asusta a un programador: volver a retocar un programa que ya esta hecho y saber si lo anterior sigue funcionando correctamente. Con nuestra prueba unitaria sabemos que si la factura tiene una base imponible de 100 (sin el descuento) el resultado por fuerza tiene que ser 116.

Si volvemos a ejecutar el test lo dará correctamente pese a la ampliación que hemos hecho con el descuento. Lo que he aplicado aquí se puede hacer perfectamente con bases de datos comprobando si tenemos descuadres de decimales, si las fechas de emisión de los documentos son correctas a partir de la forma de pago, etc.

Aparte de la función CheckEquals también tenemos estas otras:

CheckNotEquals: Comprueba si el resultado no es igual (la inversa de CheckEquals).

Check: Evalua una condición booleana.

CheckNotNull: Comprueba si la referencia a un objeto o internaz no es nula (nil).

CheckNull: Comprueba si la referencia a un objeto o internaz es nula (nil).

CheckSame: Comprueba si dos referencias a objetos o interfaces apuntan al mismo objeto el memoria.

EqualsErrorMessage: comprueba si el mensaje de error devuelto por la aplicación es igual al que le pasamos como parámetro.

Fail: Comprueba si falla una rutina.

FailEquals: Comprueba si el fallo de una rutina es igual al que le pasamos como parámetro.

FailNoEquals: Comprueba si el fallo de una rutina no es igual al que le pasamos como parámetro.

FailNotSame: Comprueba si dos errores devueltos no son iguales.

NotEqualsErrorMessage: Comprueba si el mensaje de error que le pasamos como parámetro no es igual al que devuelve la aplicación.

NotSameErrorMessage: Comprueba si el error que le pasamos como parámetro no es igual al que devuelve la aplicación.

Con todas estas funciones cubrimos todas la posibilidades en la batería de pruebas que podemos someter a un programa.

En el próximo artículo seguiremos indagando sobre RAD Studio 2007.

Pruebas realizadas en RAD Studio 2007.

Publicidad