使用简单数据类型或对象时,带有 Delphi 2010 的 PascalMock 可以正常工作。然而,有了记录,就有了麻烦。
TMock.Returns
需要一个数组TVarRec
,所以我不能只传入一个。
有一个将其转换为指针的示例,但由于它代表一个值,这对我来说没有意义。当该记录将在某些函数之间传递时,最后我将取回具有不同地址的副本。
我尝试的另一件事是将字节级别的记录复制到Variant
using 中VarArrayCreate
。当馈送到 PascalMock 时,这神秘地导致错误转换为整数。当我刚刚传入时,我得到了同样的错误Variant(TBytes.Create(1, 2, 3))
。没有明确的情况 to Variant
,我再次得到一个整数(指针?)。
当然,我总是可以完全手动编写一个模拟,但考虑到 PascalMock 的年龄,我希望这是一个已解决的问题。请注意,我什至没有使用最新AutoMockIntf
添加的内容。是否有适当的方法将任何记录转换为 PascalMocks 可以使用的 Variant 或从 Variant 转换?或者在这种情况下是否有更好的方法来处理记录?
这是我想要实现的一些示例代码:
interface
uses
SysUtils,
TestFramework,
PascalMock;
type
TMyRecord = record
// Sample fields but should work for any record structure
FirstField: Integer;
SecondField: string;
class operator Equal(const A, B: TMyRecord): Boolean;
class operator NotEqual(const A, B: TMyRecord): Boolean;
end;
IRecordMaker = interface
['{436D6FC3-3DCD-4EFF-B206-1C3E1A5561D1}']
function Make: TMyRecord;
end;
TFakeRecordMaker = class(TMock, IRecordMaker)
public
function Make: TMyRecord;
end;
IRecordSaver = interface
['{91BB1347-A34E-4A77-8993-3ADBFBE8726D}']
procedure Save(const ARecord: TMyRecord);
end;
TFakeRecordSaver = class(TMock, IRecordSaver)
public
procedure Save(const ARecord: TMyRecord);
end;
TRunner = class
private
FRecordMaker: IRecordMaker;
FRecordSaver: IRecordSaver;
public
constructor Create(ARecordMaker: IRecordMaker; ARecordSaver: IRecordSaver);
procedure Run;
end;
TestRunner = class(TTestCase)
private
FRecordMaker: TFakeRecordMaker;
FRecordSaver: TFakeRecordSaver;
protected
procedure SetUp; override;
procedure TearDown; override;
published
procedure MakesRecordAndSavesIt;
end;
implementation
{ TMyRecord }
class operator TMyRecord.Equal(const A, B: TMyRecord): Boolean;
begin
Result := (A.FirstField = B.FirstField) and (A.SecondField = B.SecondField);
end;
class operator TMyRecord.NotEqual(const A, B: TMyRecord): Boolean;
begin
Result := not (A = B);
end;
{ TFakeRecordMaker }
function TFakeRecordMaker.Make: TMyRecord;
begin
// Remember having been called and return the desired data
Result := AddCall('Make').ReturnValue;
end;
{ TFakeRecordSaver }
procedure TFakeRecordSaver.Save(const ARecord: TMyRecord);
begin
// Remember having been called and with what arguments
AddCall('Save').WithParams([ARecord]);
end;
{ TRunner }
constructor TRunner.Create(ARecordMaker: IRecordMaker; ARecordSaver: IRecordSaver);
begin
inherited Create;
FRecordMaker := ARecordMaker;
FRecordSaver := ARecordSaver;
end;
procedure TRunner.Run;
var
LRecord: TMyRecord;
begin
LRecord := FRecordMaker.Make;
FRecordSaver.Save(LRecord);
end;
{ TestRunner }
procedure TestRunner.SetUp;
begin
inherited;
FRecordMaker := TFakeRecordMaker.Create;
FRecordSaver := TFakeRecordSaver.Create;
end;
procedure TestRunner.MakesRecordAndSavesIt;
var
LRecord: TMyRecord;
LSUT: TRunner;
begin
// The test data
LRecord.FirstField := 3;
LRecord.SecondField := 'bla';
// The first collaborator serving as stub should return this data
FRecordMaker.Expects('Make').Returns(LRecord);
// The second collaborator serving as mock should be passed this data
FRecordSaver.Expects('Save').WithParams([LRecord]);
LSUT := TRunner.Create(FRecordMaker, FRecordSaver);
try
// Hopefully the SUT will get the data and pass it on
LSUT.Run;
// Check if the remembered calls match the expected ones
FRecordSaver.Verify;
finally
LSUT.Free;
end;
end;
procedure TestRunner.TearDown;
begin
FRecordSaver.Free;
FRecordMaker.Free;
inherited;
end;
initialization
RegisterTest(TestRunner.Suite);
end.