2009-04-01 18 views
10

La manera en que DUnit normalmente funciona es escribir algunos métodos publicados, y DUnit los ejecuta como pruebas. Lo que quiero hacer es un poco diferente. Quiero crear pruebas en tiempo de ejecución basadas en datos. Estoy tratando de probar un módulo particular que procesa archivos de entrada para crear archivos de salida. Tengo un conjunto de archivos de entrada de prueba con los correspondientes archivos de salida buenos conocidos. La idea es crear dinámicamente pruebas, una para cada archivo de entrada, que procesen las entradas y verifiquen las salidas con las buenas conocidas.Prueba de DUnit basada en datos

La fuente real de datos aquí, sin embargo, no es importante. La dificultad es hacer que DUnit se comporte de una manera basada en datos. Por el bien de este problema, supongamos que la fuente de datos fuera solo un generador de números aleatorios. Este es un ejemplo de un problema concreto que llega al corazón de la dificultad:

Cree algunos objetos de prueba (TTestCase o lo que sea) en tiempo de ejecución, por ejemplo, 10 de ellos, donde cada uno

  1. lleva el nombre en tiempo de ejecución de un entero generado aleatoriamente. (Por 'nombre' me refiero al nombre de la prueba que aparece en el árbol de prueba-corredor.)
  2. Pasa o falla según un número entero aleatorio. Pasa por par, falla por impar.

Desde el diseño de DUnit, se ve que fue diseñado con la suficiente flexibilidad en mente para hacer este tipo de cosas posible. No estoy seguro de que así sea. Intenté crear mi propia clase de prueba heredando TAbstractTest e ITest, pero no se pudo acceder a algunos métodos cruciales. También traté de heredar de TTestCase, pero esa clase está estrechamente ligada a la idea de ejecutar métodos publicados (y las pruebas llevan el nombre de los métodos, por lo que no podía simplemente llamar a una sola, por ejemplo, 'ir', porque entonces todas mis pruebas se llamarían 'ir', y quiero que todas mis pruebas se nombren individualmente).

O bien, ¿hay alguna alternativa a DUnit que pueda hacer lo que yo quiera?

Respuesta

17
program UnitTest1; 

{$IFDEF CONSOLE_TESTRUNNER} 
{$APPTYPE CONSOLE} 
{$ENDIF} 

uses 
    Forms, Classes, SysUtils, 
    TestFramework, 
    GUITestRunner, 
    TextTestRunner; 

{$R *.RES} 

type 
    TIntTestCase = class(TTestCase) 
    private 
    FValue: Integer; 
    public 
    constructor Create(AValue: Integer); reintroduce; 
    function GetName: string; override; 
    published 
    procedure Run; 
    end; 

{ TIntTestCase } 

constructor TIntTestCase.Create(AValue: Integer); 
begin 
    inherited Create('Run'); 
    FValue := AValue; 
end; 

function TIntTestCase.GetName: string; 
begin 
    Result := Format('Run_%.3d', [FValue]); 
end; 

procedure TIntTestCase.Run; 
begin 
    Check(FValue mod 2 = 0, Format('%d is not an even value', [FValue])); 
end; 

procedure RegisterTests; 
const 
    TestCount = 10; 
    ValueHigh = 1000; 
var 
    I: Integer; 
begin 
    Randomize; 
    for I := 0 to TestCount - 1 do 
    RegisterTest(TIntTestCase.Create(Random(ValueHigh) + 1)); 
end; 

begin 
    Application.Initialize; 
    RegisterTests; 
    if IsConsole then 
    TextTestRunner.RunRegisteredTests 
    else 
    GUITestRunner.RunRegisteredTests; 
end. 
+0

Eso es increíble. Gracias. Intentaba algo similar, pero algunos errores en el camino se sumaron para que no funcionara. Gracias de nuevo. –

+0

Me alegro de poder ayudar, aplausos. –

+0

¿Qué sugiere para tener tanto casos controlados por datos como ordinarios en la misma clase de prueba? –

2

Yo diría que básicamente quieres tener una sola función de "super-prueba" que luego llame a otras pruebas, una para cada archivo de datos. Esto es lo que hacemos con una de nuestras pruebas DUnit. Simplemente cargue cada archivo disponible a su vez en un bucle y ejecute la prueba con Check, según corresponda.

La alternativa, que también usamos en el mismo proyecto para probar la aplicación final y su carga y análisis de datos, es utilizar algo como FinalBuilder para realizar un bucle en la aplicación (presumiblemente también podría usar la aplicación DUnit y un parámetro) con los diversos archivos de datos diferentes. La aplicación se ejecuta, realiza un análisis y luego se cierra después de guardar. Luego, otra aplicación compara los datos resultantes con los datos ideales e informa un error si corresponde.

+0

La idea de la superprueba no funcionará en este caso porque cada prueba tarda un poco en ejecutarse y, por lo general, solo unas pocas se romperán a la vez. Necesitamos poder aislar solo esas pruebas para arreglar el código. Ejecutar toda la prueba súper siempre sería lamentablemente muy lento. –

+0

@dangph: Aquí es donde entra la prueba FinalBuilder. La prueba individual puede fallar, pero uso la función try/catch para registrar la falla única, pero continúo con el resto y luego fallo la construcción inmediatamente después de la última. . – mj2008

+0

@ mj2008, lo que estamos haciendo es refactorizar este módulo bastante retorcido. A medida que refaccionamos las cosas, las pruebas se romperán. Queremos ejecutar solo las pruebas rotas (a través de casillas de verificación DUnit) mientras corregimos el código porque las pruebas son bastante lentas, y normalmente tendremos que repetir el ciclo varias veces. –

Cuestiones relacionadas