delphi 使用Spring4D的模拟方法上的误导性内存泄漏

o8x7eapl  于 2023-02-15  发布在  Spring
关注(0)|答案(1)|浏览(156)

我有一个类TMyClass,我在这个类上注入了接口IFileManager,这个接口里面有一个方法GetCompanyWorkbook(const ACompanyId: System.Integer; const AStream: TStream),这个方法从ACompanyId填充AStream的depend,在真实的代码上一切都很好,但是当我运行类TMyClass的单元测试,通过 frameworkSpring 4D模拟IFileManager的时候,FastMM报告内存泄漏13-20 bytes: TIndexWrapper x 1。我使用了存储库(分支 * 主/主 *)中的最新Spring 4D版本1.26

unit Unit1.pas

interface
  DUnitX.TestFramework,
  Spring.Mocking;

type
  IFileManager = interface (IInvokable)
   
   procedure GetCompanyWorkbook(const ACompanyId: System.Integer; const AStream: TStream);
  end;

  TMyClass = class
  strict private
    FFileManager: IFileManager;
  
  public
    constructor Create(const AFileManager: IFileManager);
    procedure GenerateInvoice(const ACompanyId: System.Integer);
  end;

  [TestFixture]
  TMyClassTests = class
  strict private
    FMockStream: TStream;
    FMyClass: TMyClass;
    FFileManager: Mock<IFileManager>;

    procedure SetupFileManagerMock();
    procedure InitMockStream(const AMockFile: string);

  public
    [Setup]
    procedure Setup();

    [TearDown]
    procedure TearDown();

    [TestCase('Test invoice generation', '2|invoice_2023.xls', '|')]
    procedure TestGenerateInvoice(const ACompanyId: System.Integer; const AMockFile: string);
  end;

implementation

uses
  System.Classes,
  Spring;

constructor TMyClass.Create(const AFileManager: IFileManager);
begin
  Guard.CheckNotNull(AFileManager, 'AFileManager');
  inherited Create();
  Self.FFileManager := AFileManager;
end;

procedure TMyClass.GenerateInvoice(const ACompanyId: System.Integer);
begin
  var sTmpFile := Self.GetTempInvoiceFile(ACompanyId);
  var fs := TFileStream.Create(sTmpFile, fmCreate);
  try
    Self.FFileManager.GetComparyWorkbook(ACompanyId, fs);
    // Do some operations with stream
  finally
    fs.Free();
  end;
end;

procedure TMyClassTests.Setup();
begin
  Self.FMockStream := nil;
  Self.FMyClass := TMyClass.Create(Self.FFileManager);
end;

procedure TMyClassTests.TearDown();
begin
  Self.FMyClass.Free();
  Self.FMockStream.Free();
end;

procedure TMyClassTests.InitMockStream(const AMockFile: string);
begin
  Self.FMockStream := TFileStream.Create(AMockFile, fmOpenRead);
end;

procedure TMyClassTests.SetupFileManagerMock();
begin
  Self.FFileManager.Setup.Executes(
    function(const callInfo: TCallInfo): TValue
    begin
      callInfo.Args[1].AsType<TStream>.CopyFrom(Self.FMockStream);
    end)
    .When(Args.Any)
    .GetCompanyWorkbook(Arg.IsAny<System.Integer>, Arg.IsAny<TStream>);
end;

procedure TMyClassTests.TestGenerateInvoice(const ACompanyId: System.Integer; const AMockFile: string);
begin
  Self.InitMockStream(AMockFile);
  Self.SetupFileManagerMock();
  Assert.WillNotRaiseAny(
    procedure
    begin
      Self.FMyClass.GenerateInvoice(ACompanyId);
    end
   );
end;
eoxn13cs

eoxn13cs1#

问题是您使用的构造是多余的:

.When(Args.Any)
.GetCompanyWorkbook(Arg.IsAny<System.Integer>, Arg.IsAny<TStream>);

Args.Any传递给When或对参数使用单独的Arg匹配。传递Args.Any会导致模拟在内部忽略单独的参数匹配。这会导致用于参数匹配的临时构造对象泄漏,这一问题需要修复。

相关问题