AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign IInvokable nachträglich einem Interface hinzufügen

IInvokable nachträglich einem Interface hinzufügen

Ein Thema von WiPhi · begonnen am 3. Sep 2018 · letzter Beitrag vom 6. Sep 2018
Antwort Antwort
Seite 1 von 2  1 2   
WiPhi

Registriert seit: 19. Feb 2015
72 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

IInvokable nachträglich einem Interface hinzufügen

  Alt 3. Sep 2018, 21:30
Hallo liebe Delphianer,

Das Delphi-Mocks braucht ja zwingend Methodeninformationen ($M+) um arbeiten zu können. Nun habe ich ein Problem beim Testen diverser Klassen, welche von einem Interface abhängig sind, welches nicht von IInvokable erbt und auch keine Methodeninformationen hat.

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 , 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:
INichtAenderbaresInterface = interface // Hier fehlt das (IInvokable) oder $M+
  procedure MacheEtwas;
  procedure MacheEtwasAnderes;
end;
Und eine Klasse, welches dieses Interface benutzt
Delphi-Quellcode:
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;
Diese Klasse funktioniert in der laufenden Applikation, jedoch nicht wenn ich sie teste und das Interface mocke. Beispielcode in DUnitX:
Delphi-Quellcode:
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;
Für Ideen und Ansätze bedanke ich mich schon vorab
Wer sucht, der findet. Wer länger sucht, findet mehr.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
3.823 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#2

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 4. Sep 2018, 10:54
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.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
WiPhi

Registriert seit: 19. Feb 2015
72 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#3

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 4. Sep 2018, 16:00
... 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.
Da spuckt mir leider das TVirtualInterface in die Suppe:
Code:
"Unzureichende RTTI zur Unterstützung dieser Operation verfügbar"
Ich muss das Interface ja dann von dem ursprünglichen "erben" lassen, das ich den Cast machen kann, oder?
Delphi-Quellcode:
{$M+}
ITestNichtAenderbaresInterface = interface(INichtAenderbaresInterface)
  procedure MacheEtwas;
  procedure MacheEtwasAnderes;
end;
{$M-}
Was mich dann zu dieser Lösung bringt:
Delphi-Quellcode:
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;
... ruft auch den Testfall auf
Delphi-Quellcode:
TEineKlasse.IchSollGetestetWerden;
begin
  FBenoetigtesInterface.MacheEtwas; // <-- Jetzt knallt es hier mit einer EInsufficientRtti
  // ....
  FBenoetigtesInterface.MacheEtwasAnderes;
end;
... 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 )

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 .

Ich danke Dir aber schon mal für den Input!
Wer sucht, der findet. Wer länger sucht, findet mehr.
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#4

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 4. Sep 2018, 16:36
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.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
3.823 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#5

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 4. Sep 2018, 17:25
Nein, so sollst du das machen

***schnipp***
Gönnau
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
WiPhi

Registriert seit: 19. Feb 2015
72 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#6

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 6. Sep 2018, 11:28
Nein, so sollst du das machen
Delphi-Quellcode:
procedure Test( );
var
  mock: TMock<IMirrorFoo>;
  mirror: IMirrorFoo;
  org: IFoo;
begin
  // .....
  mirror := mock;
  org := IFoo( mirror ); // <-- WiPhi: darauf bin ich nicht gekommen, ehrlich gesagt wundert es mich das der Compiler das zulässt
  org.Foo();
Das sieht sehr gut aus! Vielen Dank.

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:
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;
Dieses IBar Interface auch nch irgendwie da rein zu mogeln kommt mir fast einer Schande gleich, dennoch die Frage ob es möglich ist.

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 )
Wer sucht, der findet. Wer länger sucht, findet mehr.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
3.823 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#7

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 6. Sep 2018, 11:39
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 org ? 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?
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie ( 6. Sep 2018 um 11:42 Uhr)
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#8

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 6. Sep 2018, 12:27
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.
  Mit Zitat antworten Zitat
WiPhi

Registriert seit: 19. Feb 2015
72 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#9

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 6. Sep 2018, 14:04
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.

Wo ist das Problem?

Vorgehen nach Schema F plus der Methode etwas Lben in die Mock-Hülle pusten
Danke! So etwas habe ich gesucht. Ich bin auf dem Gebiet der Mocks noch neu.

Danke an alle für die hilfreiche Unterstützung!
Wer sucht, der findet. Wer länger sucht, findet mehr.
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#10

AW: IInvokable nachträglich einem Interface hinzufügen

  Alt 6. Sep 2018, 14:51
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.
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +2. Es ist jetzt 19:13 Uhr.
Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf