![]() |
IInvokable nachträglich einem Interface hinzufügen
Hallo liebe Delphianer,
Das ![]() Da das Interface fest in einem Delphi-Package verankert ist, habe ich keine Möglichkeit das noch nachträglich einzubauen. (Bzw. sollte das nur der letzte Ausweg sein. Ich suche eine elegantere Lösung :-D, da ich diese Infos nur für die Tests brauche...) Nun die große Frage: Bekomme ich diese Informationen irgendwie nachträglich rein? Das Interface "abzuleiten" und für dieses die Methodenionformationen mittels $M+ hinzu zu compilieren klappt leider nicht. Ich versuche das ganze zusätzlich noch einmal zu veranschaulichen: Gegeben ist ein Interface, welches ich nicht ändern kann:
Delphi-Quellcode:
Und eine Klasse, welches dieses Interface benutzt
INichtAenderbaresInterface = interface // Hier fehlt das (IInvokable) oder $M+
procedure MacheEtwas; procedure MacheEtwasAnderes; end;
Delphi-Quellcode:
Diese Klasse funktioniert in der laufenden Applikation, jedoch nicht wenn ich sie teste und das Interface mocke. Beispielcode in DUnitX:
TEineKlasse = class
private FBenoetigtesInterface: INichtAenderbaresInterface; public procedure IchSollGetestetWerden; constructor Create(const BenoetigtesInterface: INichtAenderbaresInterface); end; ... TEineKlasse.Create(const BenoetigtesInterface: INichtAenderbaresInterface); begin FBenoetigtesInterface := BenoetigtesInterface; end; TEineKlasse.IchSollGetestetWerden; begin // Die Funktion greift direkt auf das Interface zu FBenoetigtesInterface.MacheEtwas; // .... FBenoetigtesInterface.MacheEtwasAnderes; end;
Delphi-Quellcode:
Für Ideen und Ansätze bedanke ich mich schon vorab :)
procedure TestFall.Teste;
var dummy: TMock<INichtAenderbaresInterface>; TesteEineKlasse: TEineKlasse; begin dummy := TMock<INichtAenderbaresInterface>.Create; // <-- Hier knallt es, da keine RTTI Infos TesteEineKlasse := TEineKlasse.Create(dummy); TesteEineKlasse.IchSollGetestetWerden; end; |
AW: IInvokable nachträglich einem Interface hinzufügen
Nachträglich method info ($M+) hinzufügen geht nicht, bzw es bewirkt nur, dass ab dieser Ebene RTTI für Methoden erzeugt werden.
Wenn du den Code aus dem Package wirklich nicht anfassen kannst, bliebe noch die Möglichkeit, die Interface Deklaration zu kopieren, ihm $M+ zu verpassen und das im Mock zu benutzen und dann beim Übergeben an deine zu testende Klasse zu hardcasten. |
AW: IInvokable nachträglich einem Interface hinzufügen
Zitat:
Code:
Ich muss das Interface ja dann von dem ursprünglichen "erben" lassen, das ich den Cast machen kann, oder?
"Unzureichende RTTI zur Unterstützung dieser Operation verfügbar"
Delphi-Quellcode:
Was mich dann zu dieser Lösung bringt:
{$M+}
ITestNichtAenderbaresInterface = interface(INichtAenderbaresInterface) procedure MacheEtwas; procedure MacheEtwasAnderes; end; {$M-}
Delphi-Quellcode:
... ruft auch den Testfall auf
procedure TestFall.Teste;
var dummy: TMock<ITestNichtAenderbaresInterface>; TesteEineKlasse: TEineKlasse; begin dummy := TMock<ITestNichtAenderbaresInterface>.Create; // <-- Mag er, kein Problem mehr TesteEineKlasse := TEineKlasse.Create(ITestNichtAenderbaresInterface(dummy)); // <-- Hardcast TesteEineKlasse.IchSollGetestetWerden; // Aufruf der zu testenden Methode end;
Delphi-Quellcode:
... Den Zugriff das die Interface-Methode, welche ich ihm bei $M+ gegeben habe, sieht er trotzdem nicht und versucht stattdessen die ursprüngliche aufzurufen. (Interface Methoden zu überschreiben geht ja auch nicht :D)
TEineKlasse.IchSollGetestetWerden;
begin FBenoetigtesInterface.MacheEtwas; // <-- Jetzt knallt es hier mit einer EInsufficientRtti // .... FBenoetigtesInterface.MacheEtwasAnderes; end; Ich glaube auch nicht das Du das so meintest, wie das mit der Ableitung gemacht habe. Da hab ich sicher was falsch verstanden. Aber wenn ich das Interface nicht von dem ursprünglichen ableite, gehen auch keine Typecasts mehr. Ich stehe sozusagen etwas auf dem Schlauch. Vielleicht brauche ich auch noch einen Kaffee :roll:. Ich danke Dir aber schon mal für den Input! |
AW: IInvokable nachträglich einem Interface hinzufügen
Nein, so sollst du das machen
Delphi-Quellcode:
unit Unit1;
interface type IFoo = interface ['{45486F85-F11C-47B8-A85A-328C8845CA41}'] procedure Foo( ); end; implementation end.
Delphi-Quellcode:
unit Unit2;
interface type {$M+} IMirrorFoo = interface ['{B5628A7F-7ECF-4123-A079-0193C8894185}'] procedure Foo( ); end; {$M-} implementation end.
Delphi-Quellcode:
program MockMock;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Rtti, Delphi.Mocks, Unit1 in 'Unit1.pas', Unit2 in 'Unit2.pas'; procedure Test( ); var mock: TMock<IMirrorFoo>; mirror: IMirrorFoo; org: IFoo; begin mock := TMock<IMirrorFoo>.Create( ); mock.Setup.WillExecute( 'Foo', function( const args: TArray<TValue>; const ReturnType: TRttiType ): TValue begin WriteLn( 'Foo called' ); end ); mirror := mock; org := IFoo( mirror ); org.Foo(); end; begin try Test( ); except on E: Exception do Writeln( E.ClassName, ': ', E.Message ); end; ReadLn; end. |
AW: IInvokable nachträglich einem Interface hinzufügen
Zitat:
|
AW: IInvokable nachträglich einem Interface hinzufügen
Zitat:
Leider zieht das Problem weitere Kreise. Ich habe ein Interface (in dem Package) gefunden, welches als Rückgabewert einer Funktion ein Interface liefert, welches auch keine RTTI Informationen enthält. Sobald er im Test darauf zugreifen möchte, schmiert er mit einer Zugriffsverletzung ab.
Delphi-Quellcode:
Dieses IBar Interface auch nch irgendwie da rein zu mogeln kommt mir fast einer Schande gleich, dennoch die Frage ob es möglich ist.
IBar = interface
procedure KannNichts; end; IFoo = interface function DoBar: IBar; end; {$M+} IMirrorFoo = interface function DoBar: IBar; end; {$M-} // ........ procedure TestKlasse.TesteFoo; var mock: TMock<IMirrorFoo>; mirror: IMirrorFoo; foo: IFoo; bar: IBar; begin // ... Code wie bisher ... bar := org.DoBar; // <-- Da kommt nichts mehr sinnvolles, Zugriffsverletztung end; Aber! Wahrscheinlich wäre es sinnvoller die betreffenden Interfaces zu wrappen und nur noch über diese Wrapper Interfaces zu arbeiten. Dann müsste ich zwar ziemlich viel doppelt schreiben (Wrapper und Package Interfaces), aber ich könnte das Package evtl. jederzeit ersetzen und wesentlich komfortabler testen. Wie würdet ihr damit verfahren? (Ein Glück hier gibt's so viele Profis :thumb:) |
AW: IInvokable nachträglich einem Interface hinzufügen
Bevor wir hier noch mehr rumhacken mal die Frage: über was für ein Package reden wir hier? Drittanbieter ohne Code, wo man nicht mal ebend beherzt ein {$M+} reinschreiben und rekompilieren kann?
Davon abgesehen verwirrt mich der code etwas. Was ist nun
Delphi-Quellcode:
? Das gemockte IFoo? Wenn ja, na dann hast du ihm ja wohl im mock Setup gesagt, dass er bei DoBar irgendwas sinnvolles zurück geben soll oder?
org
|
AW: IInvokable nachträglich einem Interface hinzufügen
Wo ist das Problem?
Vorgehen nach Schema F plus der Methode etwas Lben in die Mock-Hülle pusten
Delphi-Quellcode:
program MockMock;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Rtti, Delphi.Mocks; type IBar = interface ['{BFDEEA1F-182C-4009-93DA-78236C497E2E}'] procedure Bar( ); end; IFoo = interface ['{45486F85-F11C-47B8-A85A-328C8845CA41}'] function Foo( ): IBar; end; type {$M+} IMirrorBar = interface ['{AC275EFC-EED0-46A9-9409-8D11B3A86AF9}'] procedure Bar( ); end; {$M-} {$M+} IMirrorFoo = interface ['{B5628A7F-7ECF-4123-A079-0193C8894185}'] function Foo( ): IBar; end; {$M-} procedure Test( ); var foomock: TMock<IMirrorFoo>; foomirror: IMirrorFoo; foo: IFoo; barmock: TMock<IMirrorBar>; barmirror: IMirrorBar; bar: IBar; begin barmock := TMock<IMirrorBar>.Create( ); barmock.Setup.WillExecute( 'Bar', function( const args: TArray<TValue>; const ReturnType: TRttiType ): TValue begin WriteLn( 'Bar called' ); end ); barmirror := barmock; bar := IBar( barmirror ); foomock := TMock<IMirrorFoo>.Create( ); foomock.Setup.WillExecute( 'Foo', function( const args: TArray<TValue>; const ReturnType: TRttiType ): TValue begin WriteLn( 'Foo called' ); Result := TValue.From( bar ); end ); foomirror := foomock; foo := IFoo( foomirror ); foo.Foo( ).Bar( ); end; begin try Test( ); except on E: Exception do Writeln( E.ClassName, ': ', E.Message ); end; ReadLn; end. |
AW: IInvokable nachträglich einem Interface hinzufügen
Es geht um ein Drittanbieter Package zu welchem ich auch den Quellcode habe.
Das Interface anzupassen und das Package neu zu übersetzen wäre nicht das Problem. Müsste ich nur bei jedem Update des Packages den Quellcode abgleichen (was bei diesem wahrscheinlich relativ einfach wäre, da aller Interfaces zentral liegen). Mir stellt sich aber dabei die grundsätzliche Frage, wie man mit so etwas am besten umgeht. Ich habe ja auch einige Packages ohne Quellcode, dessen Interfaces ich nutze, auf die ich zugreife und testen möchte. Zitat:
Danke an alle für die hilfreiche Unterstützung! |
AW: IInvokable nachträglich einem Interface hinzufügen
Ich denke du machst da generell etwas falsch.
Eigentlich definiert man sich eigene Interfaces und verwendet diese in der Anwendung. Bei der Implementierung dieser Interfaces kann man dann auf diese Drittanbieter zurückgreifen. Dadurch kommen diese aber nur mittelbar mit der Anwendung in Berührung. Die Unit-Tests erfolgen auf jeden Fall mit den eigenen Interfaces. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:02 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz