Как получить доступ к полям TTestCase в классе TTestSetup - PullRequest
12 голосов
/ 05 февраля 2011

Я создаю модульные тесты с помощью DUnit.У меня есть класс, который занимает довольно много времени для инициализации.

Я извлекаю класс TMyTestSetup из TTestSetup и переопределяю его метод установки.Этот метод SetUp вызывается только один раз для всех тестов в моем TTestCase.Я поместил процесс инициализации в процедуру TMyTestSetup.SetUp, чтобы повысить производительность.

Моя проблема в том, как получить доступ к объекту, который я хочу инициализировать, - это поле моего TMyTest в классе TestSetup?Единственный способ сделать это, объявив это глобально?

непроверенный короткий пример:

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));

Ответы [ 6 ]

9 голосов
/ 24 марта 2012

Хитрость заключается в том, чтобы использовать переменную открытого класса в классе TMyTestSetup.

Вот так (проверено и работает, завершено) пример:

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.

Как показывают комментарии в примере, я использовал рандомизированную частную переменную и некоторые выходные данные отладочной трассы, чтобы подтвердить, что каждый вызов теста с набором тестов относится к одной и той же копии целевого объекта, но мы получаем другая копия целевого объекта при каждом запуске набора тестов.

4 голосов
/ 05 февраля 2011

Вы можете получить новый класс Test Suite из класса TTestSuite и переопределить его методы SetUp и TearDown, затем вы можете добавить свои тестовые случаи в этот конкретный набор тестов и зарегистрировать набор.

Таким образом,Методы setup и TearDown класса вашего набора тестов будут вызываться один раз, а методы SetUp и TearDown каждого теста будут вызываться для каждого метода теста, определенного в этом тесте.

Порядок выполнения будет таким:

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;
3 голосов
/ 23 октября 2012

Наличие только одного опубликованного метода, который в свою очередь вызывает все остальные методы тестирования, является ленивым, но более быстрым способом вызова процедур Setup и TearDown только один раз.

2 голосов
/ 05 февраля 2011

Вы не можете инициализировать поля TTestCase для всего набора тестов, и вот объяснение, почему:

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.

Если вы запустите вышеупомянутый модульный тест, вы увидите, что адреса «Self», показанные в методах Test1 и Test2, отличаются. Это означает, что экземпляры объекта TMyTestCase отличаются для вызовов Test1 и Test2.

Следовательно, любые поля, которые вы можете объявить в классе TMyTestCase, являются нестабильными между вызовами тестового метода.

Чтобы выполнить «глобальную» инициализацию, вы должны объявить свой объект глобально, а не как поле TMyTestCase.

1 голос
/ 06 февраля 2011

В зависимости от версии Delphi, вы можете просто сделать поле TMyTest.fTakes4Ever2Init public class var, чтобы инициализировать его из настройки теста.(Это был бы более стиль ООП по сравнению с глобальной переменной единицы.)

1 голос
/ 05 февраля 2011

Используя TTestSetup, вы можете сделать что-то вроде этого:

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])
  ));

Это немного отвратительно, но это делает работу!

...