Las DUnit y el test Programming

Cómo funciona el test unitario?
Las xUnit, crean un test por cada clase que tenga, por lo tanto testa los métodos de mi clase.
- Ejemplo clase TArithmetic:
unit Arithmetic; interface type TArithmetic = class(TObject) private Fx: integer; Fy: integer; procedure Setx(const Value: integer); procedure Sety(const Value: integer); public property x: integer read Fx write Setx; property y: integer read Fy write Sety; function plus(): integer; function minus(): integer; function multiply(): integer; function divide(): integer; function floatDivision(): double; function sinus(): double; function cosinus(): double; end; implementation { TArithmetic } function TArithmetic.cosinus: double; begin result := cos(self.Fx); end; function TArithmetic.divide: integer; begin result := Self.Fx div Self.Fy; end; function TArithmetic.floatDivision: double; begin result := Self.Fx / Self.Fy; end; function TArithmetic.minus: integer; begin result := Self.Fx - Self.Fy; end; function TArithmetic.multiply: integer; begin result := Self.Fx * Self.Fy; end; function TArithmetic.plus: integer; begin result := Self.Fx + Self.Fy; end; procedure TArithmetic.Setx(const Value: integer); begin Fx := Value; end; procedure TArithmetic.Sety(const Value: integer); begin Fy := Value; end; function TArithmetic.sinus: double; begin result := sin(self.Fx); end; end.
- Creando el Test Project:




Seleccionamos los métodos que queremos para nuestro test y luego le damos un nombre y lo asociamos a nuestro proyecto:


unit TestArithmetic; { 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, Arithmetic; type // Test methods for class TArithmetic TestTArithmetic = class(TTestCase) strict private FArithmetic: TArithmetic; public procedure SetUp; override; procedure TearDown; override; published procedure Testplus; procedure Testminus; procedure Testmultiply; procedure Testdivide; procedure TestfloatDivision; procedure Testsinus; procedure Testcosinus; end; implementation procedure TestTArithmetic.SetUp; begin FArithmetic := TArithmetic.Create; end; procedure TestTArithmetic.TearDown; begin FArithmetic.Free; FArithmetic := nil; end; procedure TestTArithmetic.Testplus; var ReturnValue: Integer; begin ReturnValue := FArithmetic.plus; // TODO: Validate method results end; procedure TestTArithmetic.Testminus; var ReturnValue: Integer; begin ReturnValue := FArithmetic.minus; // TODO: Validate method results end; procedure TestTArithmetic.Testmultiply; var ReturnValue: Integer; begin ReturnValue := FArithmetic.multiply; // TODO: Validate method results end; procedure TestTArithmetic.Testdivide; var ReturnValue: Integer; begin ReturnValue := FArithmetic.divide; // TODO: Validate method results end; procedure TestTArithmetic.TestfloatDivision; var ReturnValue: Double; begin ReturnValue := FArithmetic.floatDivision; // TODO: Validate method results end; procedure TestTArithmetic.Testsinus; var ReturnValue: Double; begin ReturnValue := FArithmetic.sinus; // TODO: Validate method results end; procedure TestTArithmetic.Testcosinus; var ReturnValue: Double; begin ReturnValue := FArithmetic.cosinus; // TODO: Validate method results end; initialization // Register any test cases with the test runner RegisterTest(TestTArithmetic.Suite); end.
Como podéis ver, ha generado una llamada a cada uno de nuestros métodos y la asignación de variables para poder realizar el test. Si ahora iniciamos el proyecto y hacemos un run para pasar las pruebas unitarias sucede lo siguiente:

Si suponemos que el valor del divisor puede ser 0, entonces tenemos que indicarle al test que posiblemente le llegará una excepción de este tipo. Para realizar esto, tenemos que hacer lo siguiente:
unit TestArithmetic; uses SysUtils; procedure TestTArithmetic.Testdivide; var ReturnValue: Integer; begin StartExpectingException(EDivByZero); ReturnValue := FArithmetic.divide; StopExpectingException(); end; procedure TestTArithmetic.TestfloatDivision; var ReturnValue: Double; begin StartExpectingException(EInvalidOp); ReturnValue := FArithmetic.floatDivision; StopExpectingException(); end;
y poner el tipo de excepción que esperamos que suceda al realizar la operación del método. Aunque nos aparezca el mensaje:


procedure TestTArithmetic.Testplus; var ReturnValue: Integer; begin FArithmetic.x := 10; FArithmetic.y := 10; ReturnValue := FArithmetic.plus; checkEquals(ReturnValue, 20); end;
Si miramos la respuesta del test unitario obtenemos:

- Cómo funciona el xtesting framework?
El programa lanza threats y los lanza en paralelo para ir más rápido. Los test han de ser repetibles, por lo tanto en cada método hay que crear el estado para el test, ejecutar el test y luego destruir el estado. Para cada método del test, se llama al SetUp y al TearDown. Cada vez que se hace una prueba, se llama a una nueva instáncia del Test/Case, por lo tanto todo es bastante independiente.
El TestSuite es agrupar un montón de TestCase. Registrando tu TestCase en una TestSuite nos permite hacer lo mismo. Lo que te propone el TestSuite es hacer un test por método, pero lo que te sugieren es hacer un test por expectación.
Los test, hay que automatizarlos. Por pequeño que sea el cambio, ejecuto el test. En los siguientes post, hablaré del Extreme Programming y de diversos sistemas para realizar pruebas unitarias y de integración. Existen muchas herramientas tales como FIT (Framework for integrated test), Mock Objects, etc. Y sobretodo tenemos que tener en cuenta los bad smells en nuestro código, como el DRY (Don't Repeat Yourself) y el YAGNI (You Ain't Gonna Need It) que hablaré un poco más de éstos en los siguientes artículos.
Espero que os sirva de ayuda!.
- Enlaces de interés:
Pruebas automatizadas con DUnit.
Comments
Post a Comment