【问题标题】:Mocking interfaces in DUnit with Delphi-Mocks and Spring4D使用 Delphi-Mocks 和 Spring4D 在 DUnit 中模拟接口
【发布时间】:2015-01-10 08:08:34
【问题描述】:

所以,我在尝试模拟第二个复合接口时遇到访问冲突错误,下面是使用 Delphi-Mocks 和 Spring4D 框架的代码示例

unit u_DB;
type
 TDBObject = class
 public
   property ID: TGUID;
 end;

 TDBCRM = class(TDBObject)
 public
   property SOME_FIELD: TSomeType;
 end;

unit i_dmServer;
type
  {$M+}
  IdmServer = interface
  ['{A4475441-9651-4956-8310-16FB710EAE5E}']
    function GetServiceConnection: TServiceConnection;
    function GetCurrentUser(): TUser;
  end;  

unit d_ServerWrapper;
type
  TdmServerWrapper = class(TInterfacedObject, IdmServer)
  private
    function GetServiceConnection: TServiceConnection;
    function GetCurrentUser(): TUser;
  protected
    FdmServer: TdmServer;
  end;

implementation

constructor TdmServerWrapper.Create();
begin
  inherited Create();
  FdmServer := TdmServer.Create(nil);
end;
end.

unit i_BaseDAL;
type
  {$M+}
  IBaseDAL<T: TDBObject, constructor> = interface
  ['{56D48844-BD7F-4FF8-A4AE-30DA1A82AD67}']
    procedure RefreshData(); ....
  end;

unit u_BaseDAL;
type
  TBaseDAL<T: TDBObject, constructor> = class(TInterfacedObject, IBaseDAL<TDBObject>)
  protected

    FdmServer: IdmServer;

  public
    procedure RefreshData();
  end;

implementation

procedure TBaseDAL<T>.Create;
begin
  FdmServer := GlobalContainer.Resolve<IdmServer>;
end;

end.

unit ChildFrame;

interface

type

  TChildFrame = class(TFrame)
  private
    fDM: IBaseDAL<TDBObject>;
    function GetDM: IBaseDAL<TDBObject>;
    procedure SetDM(const Value: IBaseDAL<TDBObject>);
  public
    constructor Create(AOwner: TComponent); override;
    property DM: IBaseDAL<TDBObject> read GetDM write SetDM;
  end;

implementation

constructor TChildFrame.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  DM := nil;
end;

function TChildFrame.GetDM: IBaseDAL<TDBObject>;
begin
  if not Assigned(fDM) then
    fDM := GlobalContainer.Resolve<IBaseDAL<TDBObject>>;
  Result := fDM;
end;

procedure TfrmCustomChildFrame.SetDM(const Value: IBaseDAL<TDBObject>);
begin
  if Assigned(fDM) then
    fDM := nil;
  fDM := Value;
end;
end.

TCRMFrame = class(TChildFrame)
  ....
end;

procedure TCRMFrame.Create
begin
 DM := GlobalContainer.Resolve('i_BaseDAL.IBaseDAL<u_DB.TDBObject>@TBaseDAL<u_DB.TDBCRM>').AsInterface as IBaseDAL<TDBObject>;
  // DM := GlobalContainer.Resolve(IBaseDAL<TomDBObject>); {Not compiled et all: "E2250 There is no overloaded version of 'Resolve' that can be called with these arguments"}
end;

注册类型

unit RegisteringTypes.pas

procedure RegTypes;

implementation

procedure RegTypes;
begin
  GlobalContainer.RegisterType<TdmServerWrapper>;
  GlobalContainer.RegisterType<TBaseDAL<TDBObject>, IBaseDAL<TDBObject>>;
  GlobalContainer.RegisterType<TBaseDAL<TDBCRM>, IBaseDAL<TDBCRM>>;

  GlobalContainer.Build;
end;

initialization
  RegTypes
end.

DUNIT TEST

type
  TestTCRM = class(TTestCase)
  private
    FFrame: TCRMFrame;
    FBaseDALMock: TMock<TBaseDAL<TDBObject>>;
    procedure Init;

  protected
    procedure SetUp; override;
  published
  end;

implementation

procedure TestTCRM.Init;
begin
  inherited;
  GlobalContainer.RegisterType<IdmServer>.DelegateTo(
    function: IdmServer
    begin
      Result := TMock<IdmServer>.Create;
    end
  );

  GlobalContainer.RegisterType<IBaseDAL<TDBCRM>>.DelegateTo(
    function: IBaseDAL<TDBCRM>
    begin
      Result := TMock<IBaseDAL<TDBCRM>>.Create;
    end
  );

  GlobalContainer.RegisterType<IBaseDAL<TDBObject>>.DelegateTo(
    function: IBaseDAL<TDBObject>
    begin
      Result := TMock<IBaseDAL<TDBObject>>.Create;
    end
  );

  GlobalContainer.Build;
end;

procedure TestTfrCRMAccountClasses.SetUp;
begin
  inherited;
  Init;
  FFrame := TCRMFrame.Create(nil); // and I got ACCESS VIOLATION HERE
end;

测试项目的完整来源在这里 - https://drive.google.com/file/d/0B6KvjsGVp4ONeXBNenlMc2J0R2M。 各位大侠,请指教我哪里错了。提前谢谢!

【问题讨论】:

  • 请提供有关您遇到的异常的更多信息。您也可以参考我们的 google 组(您可以在 Spring4D 页面上找到链接)。我可能比本页上的问答系统更能帮助您。
  • 在此处添加详细信息并交叉链接groups.google.com/forum/#!topic/spring4d/D7qPyWgcXLo
  • 请查看调用堆栈,发生异常的地方。当然不是在您标记的行中,而是在构造函数中的某个位置。你也在某处写了procedure TCRMFrame.Create。这当然是不对的。请发布正确的代码,这样我们就不必猜测了。

标签: delphi delphi-xe7 spring4d delphi-mocks


【解决方案1】:

AV 是从 Delphi.Mocks 提出来的。

这是一个重现它的最小测试用例:

procedure DelphiMocksTest;
var
  func: TFunc<IdmServer>;
  dm: IdmServer;
  i: IInitializable;
begin
  func :=
    function: IdmServer
    begin
      Result := TMock<IdmServer>.Create;
      Supports(dm, IInitializable, i); // works
    end; // TMock record goes out of scope and something happens
  dm := func();
  Supports(dm, IInitializable, i); // fails
end;

【讨论】:

  • 我们可以使用任何其他 Mocking 框架代替 Delphi-Mocks 来避免这种崩溃吗?
  • 您应该向他们报告错误。但是您也可以尝试 DSharp 模拟(虽然不确定它是否可以处理这个用例)。 Spring4D 也在工作中进行模拟,但计划在 1.2 中进行。此外,它将支持容器中的自动模拟,因此无需手动注册模拟。
【解决方案2】:

您需要在某处引用 TMock,因为模拟是在超出范围时将被清理的记录。

这应该可行:

procedure DelphiMocksTest;
var
  func: TFunc<IdmServer>;
  dm: IdmServer;
  i: IInitializable;
  mock : TMock<IdmServer>;
begin
  func := function: IdmServer
  begin
    mock := TMock<IdmServer>.Create;
    Supports(dm, IInitializable, i); // works
    result := mock; 
  end; 
  dm := func();
  Supports(dm, IInitializable, i); // fails
end;

【讨论】:

  • 如果该代码“应该工作”,那么为什么代码中的 cmets 仍然说它失败了?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 2010-12-03
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 2015-10-22
相关资源
最近更新 更多