I have a class TMyClass
, on which I inject interface IFileManager
. In this interface there is a method GetCompanyWorkbook(const ACompanyId: System.Integer; const AStream: TStream)
. This method fills AStream
depend from ACompanyId
. Everything works fine on real code, but when I run unit tests for class TMyClass
and mocked IFileManager
via framework Spring4D, FastMM reports for memory leak 13-20 bytes: TIndexWrapper x 1
. I used last Spring4D version 1.26 from repository(branch main/master)
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;
The issue is that you are using this construct which is redundant:
.When(Args.Any)
.GetCompanyWorkbook(Arg.IsAny<System.Integer>, Arg.IsAny<TStream>);
Either pass Args.Any
to When
or use individual Arg matching on the parameters.
Passing Args.Any
causes the mock internally to ignore the individual parameter matching. That causes the temporarily constructed object for the parameter matching to be leaked which is not trivial to be fixed.
Update: I was able to fix the memory leak in develop branch