2011-02-05 11 views
12

Estoy creando pruebas de unidad con DUnit. Tengo una clase que toma bastante tiempo inicializar.Cómo acceder a los campos de una TTestCase en una clase TTestSetup

Obtengo una clase TMyTestSetup de TTestSetup y anulo su método de instalación. Este método SetUp solo se llama una vez para todas las pruebas en mi TTestCase. Puse el proceso de Inicialización en la rutina TMyTestSetup.SetUp para aumentar el rendimiento.

Mi problema es cómo puedo acceder al objeto que quiero inicializar, que es un campo de mi TMyTest en la clase TestSetup? ¿Es la única manera de hacerlo declararlo globalmente?

no probado breve ejemplo:

TMyTestSetup = class(TTestSetup) 
    protected 
    procedure SetUp; override; 
end; 

TMyTest = class(TTestcase) 
public 
    fTakes4Ever2Init : TInits4Ever2Init; 
published 
    procedure Test1;  
end; 

implementation 

procedure TMyTestSetup.Setup; 
begin 
    // How can I access fTakes4Ever2Init from here? 
    fTakes4Ever2Init.create // This is the call that takes long 
end; 

procedure TMyTest.Test1; 
begin 
    fTakes4Ever2Init.DoSomething; 
end; 

initialization 
    RegisterTest(TMyTestSetup.Create(TMyTest.Suite)); 
+0

Creo que tiene esto en el camino equivocado. Creo que quieres acceder a la instancia 'TMyTestSetup' de' TMyTest', ¡pero podría estar equivocado! –

+0

De cualquier manera no tengo ni idea de cómo hacerlo realidad –

+0

Debo admitir que encuentro a DUnit un poco opaco a veces. –

Respuesta

9

El truco es utilizar una variable de clase pública en la clase TMyTestSetup.

gusta esta (probado y trabajando, completo) ejemplo:

unit TestTestUnit; 

interface 

uses 
    TestFramework, TestExtensions; 

type 
    TInits4Ever2Init = class 
    private 
    FValue: integer; 
    public 
    constructor Create; 
    procedure DoSomething1; 
    procedure DoSomething2; 
    procedure DoSomething3; 
    end; 

type 
    TMyTestSetup = class(TTestSetup) 
    public class var 
    fTakes4Ever2Init: TInits4Ever2Init; 
    protected 
    procedure SetUp; override; 
    end; 

    TMyTest = class(TTestCase) 
    published 
    procedure Test1; 
    procedure Test2; 
    procedure Test3; 
    end; 

implementation 

uses 
    SysUtils, Windows; 

{ TMyTestSetup } 

procedure TMyTestSetup.Setup; 
begin 
    fTakes4Ever2Init := TInits4Ever2Init.create; // This is the call that takes long 
end; 

{ TMyTest } 

procedure TMyTest.Test1; 
begin 
    TMyTestSetup.fTakes4Ever2Init.DoSomething1; 
end; 

procedure TMyTest.Test2; 
begin 
    TMyTestSetup.fTakes4Ever2Init.DoSomething2; 
end; 

procedure TMyTest.Test3; 
begin 
    TMyTestSetup.fTakes4Ever2Init.DoSomething3; 
end; 

{ TInits4Ever2Init } 

constructor TInits4Ever2Init.Create; 
begin 
    inherited Create; 

    // FValue and Format('%p, %d', [Pointer(Self), FValue])) are to confirm 
    // that we are talking to the same object for all the tests, 
    // but that the object is different each time we run the test suite. 

    Randomize; 
    FValue := Random(10000); 

    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.Create: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

procedure TInits4Ever2Init.DoSomething1; 
begin 
    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething1: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

procedure TInits4Ever2Init.DoSomething2; 
begin 
    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething2: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

procedure TInits4Ever2Init.DoSomething3; 
begin 
    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething3: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

initialization 
    RegisterTest(TMyTestSetup.Create(TMyTest.Suite)); 
end. 

Como las observaciones de la muestra indican, he utilizado una variable privada aleatorio, y un poco de salida de rastreo de depuración, para confirmar que cada llamada de prueba con el conjunto de pruebas es la misma copia del objeto de destino, pero que estamos obteniendo una copia diferente del objeto de destino cada vez que se ejecuta el conjunto de pruebas.

4

puede derivar una nueva clase del conjunto de pruebas de la clase TTestSuite, y anular sus métodos de montaje y desmontaje, a continuación, puede añadir sus casos de prueba a este conjunto de pruebas en particular, y registrar la suite.

De esta forma, los métodos de configuración y TearDown de su clase de suite de prueba se invocarán una vez y se invocarán los métodos SetUp y TearDown para cada caso de prueba definido para cada caso de prueba.

orden de ejecución será de la siguiente manera:

TestSuite.SetUp; 

-- TestCase1.Setup; 
---- TestCase1.Test1; 
-- TestCase1.TearDown; 
-- TestCase1.Setup; 
---- TestCase1.Test2; 
-- TestCase1.TearDown; 

-- TestCase2.Setup; 
---- TestCase2.Test1; 
-- TestCase2.TearDown; 
-- TestCase2.Setup; 
---- TestCase2.Test2; 
-- TestCase2.TearDown; 

-- TestCaseN.Setup; 
---- TestCaseN.Test1; 
-- TestCaseN.TearDown; 
-- TestCaseN.Setup; 
---- TestCaseN.Test2; 
-- TestCaseN.TearDown; 

TestSuite.TearDown; 
+0

Si entiendo este derecho, esto es exactamente lo que hago derivando TMyTestSetup de la clase TTestSetup. No creo que esto sea lo que se supone que TTestSuite debe hacer. –

+0

Creo que mezcló TTestSetup y TTestSuite –

+1

@Michael: un DUnit ITestSuite 'is-a' ITest así que debe proporcionar los métodos SetUp y TearDown, lo que hace que sea seguro suponer que se supone que debe proporcionar una forma de inicialización y limpieza. el entorno del accesorio de prueba. – mjn

1

Usando TTestSetup se podría hacer algo como esto:

type 
    TMyTestSetup = class(TTestSetup) 
    private 
    FValue: Integer; 
    protected 
    procedure SetUp; override; 
    procedure TearDown; override; 
    end; 

    TMyTestCase = class(TTestCase) 
    published 
    procedure TestSomething; 
    end; 

var 
    TestSetup: TMyTestSetup; 

procedure TMyTestSetup.SetUp; 
begin 
    inherited; 
    TestSetup := Self; 
    FValue := 42; 
end; 

procedure TMyTestSetup.TearDown; 
begin 
    TestSetup := nil; 
    inherited; 
end; 

procedure TMyTestCase.TestSomething; 
begin 
    CheckEquals(TestSetup.FValue, 42); 
end; 

initialization 
    TestFramework.RegisterTest(TMyTestSetup.Create(
    TTestSuite.Create('My test suite', [TMyTestCase.Suite]) 
)); 

Se siente la mente un tanto repugnante, pero hace el trabajo!

+0

esto no es mucho mejor que declarar fTakes4Ever2Init globalmente, ¿o sí? –

+0

@Michael No realmente! Poco a poco fui llegando a esa realización. Supongo que la única diferencia es que se llama a SetUp/TearDown cada vez que ejecuta algunas pruebas. Personalmente, probablemente solo usaría un global. –

2

No se puede inicializar campos TTestCase para un conjunto de pruebas de conjunto, y aquí es una explicación de por qué:

unit Tests3; 

interface 

uses 
    TestFramework, TestExtensions, Windows, Forms, Dialogs, Controls, Classes, 
    SysUtils, Variants, Graphics, Messages; 

type 
    TMyTestCase = class(TTestCase) 
    private 
    FValue: Integer; 
    published 
    procedure Test1; 
    procedure Test2; 
    end; 

implementation 

{ TMyTestCase } 

procedure TMyTestCase.Test1; 
begin 
    FValue:= 99; 
    ShowMessage(Format('%p, %d', [Pointer(Self), FValue])); 
end; 

procedure TMyTestCase.Test2; 
begin 
    ShowMessage(Format('%p, %d', [Pointer(Self), FValue])); 
end; 

initialization 
    RegisterTest(TMyTestCase.Suite); 
end. 

Si ejecuta la prueba de la unidad anterior se verá que las direcciones del 'yo' muestran en Prueba1 y los métodos Test2 son diferentes. Eso significa que las instancias del objeto TMyTestCase son diferentes para las llamadas Test1 y Test2.

En consecuencia, todos los campos que puede declarar en la clase TMyTestCase son volátiles entre las llamadas al método de prueba.

Para realizar la inicialización "global" debe declarar su objeto globalmente, no como campo TMyTestCase.

+0

Pero, ¿y si haces de fTakes4Ever2Init un campo de la clase TTestSetup? –

+0

@Michael: estoy de acuerdo con la respuesta de David; eso es posible, pero no se puede acceder a los campos TTestSetup desde los métodos TTestCase sin crear una referencia global a la instancia de TTestSetup o algo así. – kludg

1

Dependiendo de su versión de Delphi, puede simplemente hacer que el campo TMyTest.fTakes4Ever2Init sea public class var para inicializarlo desde la configuración de prueba. (Esto sería más estilo OOP en comparación con una variable global de la unidad.)

3

Tener solo un método publicado, que a su vez llame a todos sus otros métodos de prueba es el lento pero más rápido de tener el procedimiento de instalación y TearDown llamado solo una vez.

Cuestiones relacionadas