2015-05-20 3 views
2

Я изо всех сил стараюсь издеваться над событием Spring4d с DUnit.Как Mock Spring4D События с DUnit

На самом деле я еще издевается макет возвращение издеваться о событии ...

Это основная структура.

TMyObject --EventContainer--> TMock<IEventContainer> --Event--> TMock<IEvent> 

TMyObject имеет свойство EventContainer: IEventContainer

IEventContainer имеет свойство Event: IMyEvent

Я хочу издеваться

MyObject.EventContainer.Event.Add 

Я проверил каждую возможность я мог думать. Я либо получаю AVs, либо Invalid Casts. Я поставил исходный код ниже. Если бы кто-нибудь мог помочь мне получить эту работу, это было бы действительно здорово!

program Project2; 

{$APPTYPE CONSOLE} 
{$R *.res} 

uses 
    System.SysUtils, 
    DUnitTestRunner, 
    Spring.Events, 
    Spring, 
    Classes, 
    TestFramework, 
    Delphi.Mocks; 
    //Unit1 in 'Unit1.pas'; 

type 

{$M+} 
    IMyEvent = interface(IEvent<TNotifyEvent>) 
     procedure Add(const handler: TMethod); 
    end; 
{$M-} 
{$M+} 

    IMyEventMock = interface(IMyEvent) 
     procedure Add(const handler: TMethod); 
    end; 
{$M-} 
{$M+} 

    IEventContainer = interface(IInterface) 
     function GetEvent: IMyEvent; 
     procedure SetEvent(const Value: IMyEvent); 
     property Event: IMyEvent 
      read GetEvent 
      write SetEvent; 
    end; 
{$M-} 
{$M+} 

    ITestEventContainer = interface(IEventContainer) 
     function GetEvent: TMock<IMyEvent>; 
     procedure SetEvent(const Value: TMock<IMyEvent>); 
     property Event: TMock<IMyEvent> 
      read GetEvent 
      write SetEvent; 
    end; 
{$M-} 
{$REGION 'TEventContainer'} 

    TEventContainer = class(TInterfacedObject, IEventContainer) 

    private 
     FAEvent: IMyEvent; 
     function GetEvent: IMyEvent; 
     procedure SetEvent(const Value: IMyEvent); 

    public 
     property Event: IMyEvent 
      read GetEvent 
      write SetEvent; 
    end; 

{$ENDREGION} 
{$REGION 'TMyObject'} 

    TMyObject = class(TObject) 
    private 
     FEventContainer: IEventContainer; 
     function GetEventContainer: IEventContainer; 
     procedure SetEventContainer(const Value: IEventContainer); 

    public 
     property EventContainer: IEventContainer 
      read GetEventContainer 
      write SetEventContainer; 
    end; 

{$ENDREGION} 
{$REGION 'TMyObjectTest'} 

    TMyObjectTest = class(TTestCase) 
    strict private 
     FMyObject: TMyObject; 
     FMyEventContainerMock: TMock<IEventContainer>; 
     FMyTestEventContainerMock: TMock<ITestEventContainer>; 
     FEventMock: TMock<IMyEventMock>; 

    public 
     procedure SetUp; override; 
     procedure TearDown; override; 

    published 
     procedure Test_InstanceAsValue; 
     procedure Test_Value_Make; 
     procedure Test_Value_From; 
     procedure Test_Value_From_Instance; 
     procedure Test_Value_From_Variant; 
     procedure Test_Value_From_Variant_Instance; 
     procedure Test_Mocked_Container_Value_Make; 
     procedure Test_Mocked_Container_Value_From; 
     procedure Test_Mocked_Container_Value_From_Instance; 
     procedure Test_Mocked_Container_Value_From_Variant; 
     procedure Test_Mocked_Container_Value_From_Variant_Instance; 
    end; 
{$ENDREGION} 


{$REGION 'TEventContainer'} 

function TEventContainer.GetEvent: IMyEvent; 
begin 
    Result := FAEvent; 
end; 

procedure TEventContainer.SetEvent(const Value: IMyEvent); 
begin 
    FAEvent := Value; 
end; 
{$ENDREGION} 

{$REGION 'TMyObject'} 

function TMyObject.GetEventContainer: IEventContainer; 
begin 
    Result := FEventContainer; 
end; 

procedure TMyObject.SetEventContainer(const Value: IEventContainer); 
begin 
    FEventContainer := Value; 
end; 
{$ENDREGION} 

{$REGION 'TMyObjectTest'} 

procedure TMyObjectTest.SetUp; 
begin 
    inherited; 

    FMyObject := TMyObject.Create; 

    FMyEventContainerMock := TMock<IEventContainer>.Create; 

    FMyObject.EventContainer := FMyEventContainerMock; 

end; 

procedure TMyObjectTest.TearDown; 
begin 
    inherited; 

    FMyObject.Free; 

    FMyObject := nil; 
end; 

procedure TMyObjectTest.Test_Value_Make; 
var aValue : TValue; 
begin 
    FEventMock := TMock<IMyEventMock>.Create; 

    TValue.Make(@FEventMock, TypeInfo(IMyEventMock), aValue); 

    FMyEventContainerMock.SetUp.WillReturnDefault('GetEvent', aValue); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_InstanceAsValue; 
begin 
    FEventMock := TMock<IMyEventMock>.Create; 

    FMyEventContainerMock.SetUp.WillReturnDefault('GetEvent', FEventMock.InstanceAsValue); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Mocked_Container_Value_From; 
begin 

    FMyTestEventContainerMock := TMock<ITestEventContainer>.Create; 

    FMyObject.EventContainer := FMyTestEventContainerMock; 

    FEventMock := TMock<IMyEventMock>.Create; 

    FMyTestEventContainerMock.SetUp.WillReturnDefault('GetEvent', FEventMock.InstanceAsValue); 

    FMyObject.EventContainer.Event; 

end; 

procedure TMyObjectTest.Test_Mocked_Container_Value_From_Instance; 
begin 
FMyTestEventContainerMock := TMock<ITestEventContainer>.Create; 

    FMyObject.EventContainer := FMyTestEventContainerMock; 

    FEventMock := TMock<IMyEventMock>.Create; 

    FMyTestEventContainerMock.SetUp.WillReturnDefault('GetEvent', TValue.From(FEventMock)); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Mocked_Container_Value_From_Variant; 
begin 
    FMyTestEventContainerMock := TMock<ITestEventContainer>.Create; 

    FMyObject.EventContainer := FMyTestEventContainerMock; 

    FEventMock := TMock<IMyEventMock>.Create; 

    FMyTestEventContainerMock.SetUp.WillReturnDefault('GetEvent', TValue.FromVariant(FEventMock)); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Mocked_Container_Value_From_Variant_Instance; 
begin 
    FMyTestEventContainerMock := TMock<ITestEventContainer>.Create; 

    FMyObject.EventContainer := FMyTestEventContainerMock; 

    FEventMock := TMock<IMyEventMock>.Create; 

    FMyTestEventContainerMock.SetUp.WillReturnDefault('GetEvent', TValue.FromVariant(FEventMock.Instance)); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Mocked_Container_Value_Make; 
var aValue : TValue; 
begin 
    FMyTestEventContainerMock := TMock<ITestEventContainer>.Create; 

    FMyObject.EventContainer := FMyTestEventContainerMock; 

    FEventMock := TMock<IMyEventMock>.Create; 

    TValue.Make(@aValue, TypeInfo(TMock<IMyEventMock>), aValue); 

    FMyTestEventContainerMock.SetUp.WillReturnDefault('GetEvent', aValue); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Value_From; 
begin 
    FEventMock := TMock<IMyEventMock>.Create; 

    FMyEventContainerMock.SetUp.WillReturnDefault('GetEvent', TValue.From(FEventMock)); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Value_From_Instance; 
begin 
    FEventMock := TMock<IMyEventMock>.Create; 

    FMyEventContainerMock.SetUp.WillReturnDefault('GetEvent', TValue.From(FEventMock.Instance)); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Value_From_Variant; 
begin 
    FEventMock := TMock<IMyEventMock>.Create; 

    FMyEventContainerMock.SetUp.WillReturnDefault('GetEvent', TValue.FromVariant(FEventMock)); 

    FMyObject.EventContainer.Event; 
end; 

procedure TMyObjectTest.Test_Value_From_Variant_Instance; 
begin 
    FEventMock := TMock<IMyEventMock>.Create; 

    FMyEventContainerMock.SetUp.WillReturnDefault('GetEvent', TValue.FromVariant(FEventMock.Instance)); 

    FMyObject.EventContainer.Event; 
end; 

begin 
    RegisterTest(TMyObjectTest.Suite); 
    try 
     DUnitTestRunner.RunRegisteredTests; 
     ReadLn; 
    except 
     on E: Exception do 
     begin 
      Writeln(E.ClassName, ': ', E.Message); 
      ReadLn; 
     end; 
    end; 

end. 

ответ

5

Во-первых, ваш подход неправильный. Наследование интерфейса, а затем добавление {$M+} будет включать только информацию о методе для добавленных методов. Это означает, что даже если вы добавите метод с той же сигнатурой, что и родительский интерфейс, не будет делать макет работы, потому что код все равно вызовет метод родительских интерфейсов, а не тот, который вы добавили.

Кроме того, DelphiMocks в этом случае является жертвой ошибки в преобразовании TValue интерфейса в его родительский тип. Это просто не поддерживается - см. Rtti.ConvIntf2Intf.

Я бы предложил собрать Spring4D с IEvent, наследующим от IInvokable, чтобы получить информацию о методе и избежать наследования от него.

Если вы делаете, что следующие тесты пройдут - все остальные просто мимо фиктивные неправильно:

Test_InstanceAsValue; 
Test_Value_From_Instance; 
Test_Mocked_Container_Value_From; 

В Spring4D 1.2 мы вводим новую библиотеку перехвата, которая используется также для нашего насмешливого решения. Также контейнер сможет обеспечить автопиляцию. Таким образом, вы можете написать свой тест, как это:

var 
    container: TContainer; 
    event: IMyEvent; 
begin 
    container := TContainer.Create; 
    container.AddExtension<TAutoMockExtension>; 
    try 
    FMyObject.EventContainer := container.Resolve<ITestEventContainer>; 

    event := FMyObject.EventContainer.Event; 
    event.Add(nil); 
    finally 
    container.Free; 
    end; 
end; 

Контейнер будет создавать издевается для любого типа он должен решить, что он не знает. В этом тесте вы можете зарегистрировать класс, который хотите проверить, и контейнер автоматически вводит любую зависимость как макет.

var 
    container: TContainer; 
    event: IMyEvent; 
begin 
    container := TContainer.Create; 
    container.AddExtension<TAutoMockExtension>; 
    container.RegisterType<TMyObject>.InjectProperty('EventContainer'); 
    container.Build; 
    try 
    FMyObject := container.Resolve<TMyObject>; 

    event := FMyObject.EventContainer.Event; 
    event.Add(nil); 
    finally 
    container.Free; 
    end; 
end; 

Вы можете пойти еще дальше и интегрировать контейнер автоматически насмешливый в базовом классе тестовый:

program Project2; 

{$APPTYPE CONSOLE} 

uses 
    Classes, 
    SysUtils, 
    DUnitTestRunner, 
    TestFramework, 
    Spring.Events, 
    Spring, 
    Spring.Container, 
    Spring.Container.Registration, 
    Spring.Container.AutoMockExtension, 
    Spring.Mocking; 

type 
    IMyEvent = IEvent<TNotifyEvent>; 

    IEventContainer = interface(IInvokable) 
    function GetEvent: IMyEvent; 
    procedure SetEvent(const Value: IMyEvent); 
    property Event: IMyEvent read GetEvent write SetEvent; 
    end; 

    TMyObject = class(TObject) 
    private 
    FEventContainer: IEventContainer; 
    public 
    property EventContainer: IEventContainer read FEventContainer write FEventContainer; 
    end; 

    TAutoMockingTestCase<T: class> = class(TTestCase) 
    protected 
    fContainer: TContainer; 
    fSUT: T; 
    procedure SetUp; overload; override; 
    procedure TearDown; override; 
    procedure SetUp(const registration: TRegistration<T>); reintroduce; overload; virtual; 
    end; 

    TMyTest = class(TAutoMockingTestCase<TMyObject>) 
    protected 
    procedure SetUp(const registration: TRegistration<TMyObject>); override; 
    published 
    procedure Test_EventAdd; 
    end; 

procedure TAutoMockingTestCase<T>.SetUp(const registration: TRegistration<T>); 
begin 
end; 

procedure TAutoMockingTestCase<T>.SetUp; 
begin 
    inherited; 
    fContainer := TContainer.Create; 
    fContainer.AddExtension<TAutoMockExtension>; 
    SetUp(fContainer.RegisterType<T>); 
    fContainer.Build; 
    fSUT := fContainer.Resolve<T>; 
end; 

procedure TAutoMockingTestCase<T>.TearDown; 
begin 
    fSUT.Free; 
    fContainer.Free; 
    inherited; 
end; 

procedure TMyTest.SetUp(const registration: TRegistration<TMyObject>); 
begin 
    registration.InjectProperty('EventContainer'); 
end; 

procedure TMyTest.Test_EventAdd; 
begin 
    fSUT.EventContainer.Event.Add(nil); 
end; 

begin 
    RegisterTest(TMyTest.Suite); 
    try 
    DUnitTestRunner.RunRegisteredTests; 
    ReadLn; 
    except 
    on E: Exception do 
    begin 
     Writeln(E.ClassName, ': ', E.Message); 
     ReadLn; 
    end; 
    end; 
end. 
+0

Wow. Спасибо. Я попробую это. Могу ли я попробовать библиотеку перехвата в последнем мастер-коммите spring4d? – Ludo

+0

Нет, в разработке мастер всегда будет содержать только выпущенные версии. –