Delphi mocking with nullable types
delphi, mocking, spring4d
Solution
The correct code should look like this (not using type inference):
LMock.Setup.WillReturn<TNullableInteger>(123).Any.WhenCalling.Id;
LMock.Setup.WillReturn<TNullableInteger>(456).Any.WhenCalling.ObjectId;
LMock.Setup.WillReturn<TNullableString>('blahblah').Any.WhenCalling.ObjectName;
As addition to the correct answer that Rick already gave here some explanation why that is the case:
The original code uses type inference for the values passed to WillReturn and it infers them as `ShortInt`, `SmallInt` (compiler uses the smallest signed ordinal type that fits) and `string`. These are stored as `TValue` and returned when the method is being called. But then the RTTI tries to cast the `TValue` to the return type and the method which fails because it does not know how to cast a `ShortInt` or `SmallInt` to a `Nullable<Integer>` or a `string` to a `Nullable<string>` since `TValue.TryCast` does not know about any Implicit operator overloads of the `Nullable<>` type and there is no way to register any custom converters.
Problem
What is the best way to setup Delphi DSharp mocks with Nullable types from the Spring framework? I've tried various approaches and I know I'm probably missing something really rudimentary, but I just can't figure out how to get the following code to work: ``` program DSharkMockNullable; {$APPTYPE CONSOLE} {$R *.res} uses DSharp.Testing.Mock, Spring, System.SysUtils; type {$M+} IBaseMock = interface ['{B3311345-3C1F-47E3-8235-57B1BA04E957}'] function GetId: TNullableInteger; procedure SetId(Value: TNullableInteger); property Id: TNullableInteger read GetId write SetId; end; {$M+} IMockMe = interface(IBaseMock) ['{07F8F233-E8F5-4743-88C5-97A66BB01E29}'] function GetObjectId: TNullableInteger; procedure SetObjectId(Value: TNullableInteger); property ObjectId: TNullableInteger read GetObjectId write SetObjectId; function GetObjectName: TNullableString; procedure SetObjectName(Value: TNullableString); property ObjectName: TNullableString read GetObjectName write SetObjectName; end; TMyObject = class public function ObjectIdAsString(const AObject: IMockMe): string; end; { TMyObject } function TMyObject.ObjectIdAsString(const AObject: IMockMe): string; var LId: Integer; LObjectId: Integer; LObjectName: string; begin LId := AObject.Id; // ***** FAILS HERE with an "Invalid Typecast" error LObjectId := AObject.ObjectId; LObjectName := AObject.ObjectName; Result := Format('Id: %d', [LId]); Result := Format('Object Id: %d', [LObjectId]); Result := Format('Object Name: %s', [LObjectName]); end; var LMock: Mock<IMockMe>; LObject: TMyObject; begin try LMock.Setup.WillReturn(123).Any.WhenCalling.Id; LMock.Setup.WillReturn(456).Any.WhenCalling.ObjectId; LMock.Setup.WillReturn('blahblah').Any.WhenCalling.ObjectName; LObject := TMyObject.Create; Writeln(LObject.ObjectIdAsString(LMock.Instance)); LObject.Free; Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. ``` Any ideas or suggestions would be great? I'm using Delphi XE5. Thanks. Rick.