Как сломать зависимость, чтобы включить юнит-тестирование - PullRequest
2 голосов
/ 29 декабря 2011

Я провел много времени, думая о юнит-тестировании.По крайней мере, я купил Эффективно работающий с устаревшим кодом в виде электронной книги.Большая часть этого имеет смысл, кажется, это отличная книга о тестировании модулей старого кода.Но все же я думаю, что мне нужна отправная точка для этого, так как наш проект Attracs велик.См. Также мой родовой вопрос о юнит-тестировании.

Приложение имеет модель UML для определения классов, атрибутов и отношений и использования Bold для Delphi.После каждого изменения модели мы совершаем кругосветное путешествие.При этом автоматически создаются объявления для методов в файлах businessclasses.pas и BusinessClasses_Interface.inc.SQL-скрипт также генерируется, если изменение требует изменений в базе данных.Это хорошо работало в течение многих лет, но мы никогда не использовали ни одного тестирования юнитов.

Итак, я добавляю новый тест-проект, тогда зависимости вызывают проблемы.Я получил

[Ошибка DCC] Attracs_Interface_Uses.inc (10): F1026 Файл не найден: 'MsxSupport.dcu'

Итак, чтобы подвести итог ошибки

AttracsTest.dpr использует
BusinessClasses.pas, которые используют
BusinessClasses_Interface.inc, которые используют
Attracs_Interface_Uses.inc

Так, как я могу разорвать цепочку зависимостей?

Обратите внимание, что на самом деле файлы намного больше.В модели более 300 классов, а в businessClasses.pas более 53000 строк кода ... В качестве тестового примера у меня есть только класс TPerson с методом AddResponsibility.Но вы должны понимать принцип.

Вот мои файлы:

AttracsTest.dpr

program AttracsTests;
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
  Forms,
  TestFramework,
  GUITestRunner,
  TextTestRunner,
  BusinessClasses in '..\..\server\code\BusinessClasses.pas',
  TestBusinessClasses in 'TestBusinessClasses.pas',
  ArrayOfObject in '..\..\server\code\ArrayOfObject.pas';

{$R *.RES}

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

TestBusinessClasses.pas

unit TestBusinessClasses;

interface

uses
  TestFramework,
  ArrayOfObject,
  AttracsAttributes,
  AttracsDefs,
  atXMLObjModel,
  BoldAttributes
  BoldDBInterfaces,
  BoldDefs,
  BoldDeriver,
  BoldDomainElement,
  BoldElements,
  BoldSubscription,
  BoldSystem,
  BoldSystemRT,
  BusinessClasses,   // Trigger the dependency, but also contain info about the classes get and set methods for attributes.  
  Classes,
  Contnrs,
  SysUtils,
  XMLIntf,
  XMLObjModel,
  XMLParser;

type
  TestTPerson = class(TTestCase)
  strict private
    FPerson: TPerson;
  public
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure TestAddResponsibility;
  end;

implementation

procedure TestTPerson.SetUp;
begin
  FPerson := TPerson.Create;
end;

procedure TestTPerson.TearDown;
begin
  FPerson.Free;
  FPerson := nil;
end;

procedure TestTPerson.TestAddResponsibility;
var
  ReturnValue: Boolean;
  aSession: TLogonSession;
  aDevType: TDevTypeDef;
  aMarketArea: TMarketArea;
begin
  // TODO: Setup method call parameters
  ReturnValue := FPerson.AddResponsibility(aMarketArea, aDevType, aSession);
  // TODO: Validate method results
end;

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

Attracs_Interface_Uses

AttracsDefs,
atXMLObjModel,
XMLObjModel,
XMLParser,
Contnrs,
XMLIntf,
ArrayOfObject,
BoldDBInterfaces,
MsxSupport         // Line that compiler complain about

BusinessClasses_Interface.inc

(*****************************************)
(*      This file is autogenerated       *)
(*   Any manual changes will be LOST!    *)
(*****************************************)

{$IFNDEF BusinessClasses_Interface.inc}
{$DEFINE BusinessClasses_Interface.inc}

{$IFNDEF BusinessClasses_unitheader}
unit BusinessClasses;
{$ENDIF}

{$INCLUDE Attracs.inc} //PATCH

interface

uses
  // interface uses
  {$INCLUDE Attracs_Interface_Uses.inc} ,
  // interface dependencies
  // attribute classes
  AttracsAttributes,
  BoldAttributes,
  // other
  Classes,
  SysUtils,
  BoldDefs,
  BoldSubscription,
  BoldDeriver,
  BoldElements,
  BoldDomainElement,
  BoldSystemRT,
  BoldSystem;

type
  { forward declarations of all classes }
  TPerson = class;

  TPerson = class(TAmStateObject)
  public
    function AddResponsibility(aMarketArea: TMarketArea; aDevType: TDevTypeDef; aSession: TLogonSession): Boolean; 
  end;

function GeneratedCodeCRC: String;

implementation

uses
  // implementation uses
  {$INCLUDE Attracs_Implementation_Uses.inc} ,
  // implementation dependencies
  // other
  BoldGeneratedCodeDictionary;

{$ENDIF}

Businessclasses.pas

    (*****************************************)
    (*      This file is autogenerated       *)
    (*   Any manual changes will be LOST!    *)
    (*****************************************)

    unit BusinessClasses;

    {$DEFINE BusinessClasses_unitheader}
    {$INCLUDE BusinessClasses_Interface.inc}

    { Includefile for methodimplementations 
      Have concrete implementation of methods}
    {$INCLUDE Person.inc}

    // Some get and set methods fopr attributes in the class

    // attribute FirstName
    function TPerson._Get_M_FirstName: TBAString;
    begin
      assert(ValidateMember('TPerson', 'FirstName', 14, TBAString));
      Result := TBAString(BoldMembers[14]);
    end;

    function TPerson._GetFirstName: String;
    begin
      Result := M_FirstName.AsString;
    end;

    procedure TPerson._SetFirstName(const NewValue: String);
    begin
      M_FirstName.AsString := NewValue;
    end;

    procedure InstallBusinessClasses(BoldObjectClasses: TBoldGeneratedClassList);
    begin
      BoldObjectClasses.AddObjectEntry('Person', TPerson);
    end;

    var
      CodeDescriptor: TBoldGeneratedCodeDescriptor;

    initialization
      CodeDescriptor := GeneratedCodes.AddGeneratedCodeDescriptorWithFunc('BusinessClasses', InstallBusinessClasses, InstallObjectListClasses, GeneratedCodeCRC);
    finalization
      GeneratedCodes.Remove(CodeDescriptor);
    end.

person.inc

function TPerson.AddResponsibility(aMarketArea: TMarketArea; aDevType: TDevTypeDef; aSession: TLogonSession): Boolean;
var
  vOCL: String;
  vDevResponse: TDevResponsible;
begin
  vOCL := Format('DevResponsible.allinstances->select((devType.TypeName = ''%s'') and (marketArea.name = ''%s''))->first',
                         [aDevType.TypeName, aMarketArea.name]);
  vDevResponse := GetApplicationKernel.EvaluateExpressionAsDirectElement(vOCL) as TDevResponsible;

  if not Assigned(vDevResponse) then
    vDevResponse := GetApplicationKernel.CreateAMObject('DevResponsible') as TDevResponsible;

  if Assigned(vDevResponse) then
  begin
    vDevResponse.marketArea := aMarketArea;
    vDevResponse.devType := aDevType;
    vDevResponse.responsiblePers := self;
    NotifyModificationHistory(Now, aSession, Format('Responsible for %s marketarea: %s', [aDevType.TypeName, aMarketArea.Name]));
    Result := True;
  end
  else
    Result := False;
end;

1 Ответ

1 голос
/ 29 декабря 2011

Вещи, которые я бы сделал:

  • разветвите проект, чтобы все изменения можно было сделать в безопасной «песочнице»
  • запустите cnWizards Uses Cleaner (или аналогичный инструмент) дляочистка зависимостей модулей
  • добавьте все необходимые модули в тестовый проект dpr для их документирования, за исключением сторонних библиотек, которые находятся по пути к библиотеке и являются известными зависимостями
  • составляют планв зависимости от доступных ресурсов: ищите зависимости, которые являются неожиданными (для более глубокого анализа) или которые являются низко висящими плодами (могут быть удалены без риска).Непрерывная интеграция с использованием ограничительных сценариев сборки, которые задают только пути к библиотекам для необходимых сторонних библиотек, может сильно помочь (сборка будет прерываться всякий раз, когда разработчик вводит новую зависимость)
  • время от времени слияние «безопасное» изменяется обратносоединить и начать новую итерацию, используя последнюю версию соединительной линии
...