Spring4d незарегистрированный тип интерфейса (spring4d, dunitx, delphi-mocks)

Я пишу один из моих первых юнит-тестов с использованием spring4d, dunitx и delphi-mocks. (spring4d Release 1.1 - 12.09.2014)

В моем тестовом приложении я автоматически подключаю интерфейс к моей тестируемой системе (sut):

IMyInterface = interface [{yes, a GUID here}]
  function GetSomething1: TSomething1;
  function GetSomething2: TSomething2;
end;

TMyClass = class
private
    [Inject]
    FMyInterface: IMyInterface;
    // ...
end;

Теперь, когда я использую юнит-тесты с имитацией, я использую следующий (очень простой) код:

TMyTest = class
private
    [Test]
    procedure Test1;

    [Test]
    procedure Test2;
    // ...
end;

procedure TMyTest.Test1;
var
    aSut: TMyClass;
    aIntfMock: TMock<IMyInterface>;
    aSomething1: TSomething1;
begin
    // Arrange
    GlobalContainer.RegisterType<TMyClass>;

    aIntfMock := TMock<IMyInterface>.Create;
    try
        GlobalContainer.RegisterType<IMyInterface>.DelegateTo(
            function: IMyInterface
            begin
                Result := aIntfMock;
            end)
            .AsDefault;

        GlobalContainer.Build;

        aSomething1 := TSomething1.Create;
        try
            aIntfMock.Setup.WillReturn(TValue.From<TSomething1>(aSomething1)).When.GetSomething1;

            aSut := GlobalContainer.Resolve<TMyClass>;
            try
                // Act
                aSut.DoSomething;
            finally
                 FreeAndNil(aSut);
            end;

            // Assert
            // ...
        finally
            FreeAndNil(aSomething1);
        end;
    finally
        aIntfMock.Free; 
    end;
end;

procedure TMyTest.Test2;
var
    aSut: TMyClass;
    aIntfMock: TMock<IMyInterface>;
    aSomething2: TSomething2;
begin
    // Arrange
    GlobalContainer.RegisterType<TMyClass>;

    aIntfMock := TMock<IMyInterface>.Create;
    try
        GlobalContainer.RegisterType<IMyInterface>.DelegateTo(
            function: IMyInterface
            begin
                Result := aIntfMock;
            end)
            .AsDefault;


        GlobalContainer.Build;   // <---- ERegistrationException here

        aSomething2 := TSomething1.Create;
        try
            aIntfMock.Setup.WillReturn(TValue.From<TSomething2>(aSomething2)).When.GetSomething2;

            aSut := GlobalContainer.Resolve<TMyClass>;
            try
                // Act
                aSut.DoSomething;
            finally
                 FreeAndNil(aSut);
            end;

            // Assert
            // ...
        finally
            FreeAndNil(aSomething2);
        end;
    finally
        aIntfMock.Free; 
    end;
end;

Первый метод тестирования (Test1) работает нормально... но во втором методе тестирования (Test2) в строке с GlobalContainer.Build spring4d возникает исключение: ERegistrationException('Обнаружено повторяющееся имя службы: IMyInterface_u.IMyInterface@IMyInterface_u.IMyInterface_u.IMyInterface' ').

Есть ли возможность отменить регистрацию aIntfMock, чтобы я мог зарегистрировать новый для каждой другой тестовой процедуры?

[Править] Таким образом, решение будет:

TMyTest = class
private
    FTestContainer: TContainer;
    FIntfMock: TMock<IMyInterface>;
    FSut: TMyClass;

    [Setup]
    procedure Setup;
    [TearDown]
    procedure TearDown;
    [Test]
    procedure Test1;

    [Test]
    procedure Test2;
    // ...
end;

//---------------------

procedure TMyTest.Setup;
begin
    FTestContainer := TContainer.Create;
    FTestContainer.RegisterType<TMyClass>;

    FIntfMock := TMock<IMyInterface>.Create;    

    FTestContainer.RegisterType<IMyInterface>.DelegateTo(
        function: IMyInterface
        begin
            Result := FIntfMock;
        end)
        .AsDefault;

    FTestContainer.Build;

    FSut := FTestContainer.Resolve<TMyClass>;
end;

procedure TMyTest.TearDown;
begin
    FreeAndNil(FSut);
    FIntfMock.Free;
    FreeAndNil(FTestContainer);
end;

procedure TMyTest.Test1;
var
    aSomething1: TSomething1;
begin
    // Arrange

    aSomething1 := TSomething1.Create;
    try
        aIntfMock.Setup.WillReturn(TValue.From<TSomething1>(aSomething1)).When.GetSomething1;

        // Act
        FSut.DoSomething;

        // Assert
        // ...

    finally
        FreeAndNil(aSomething1);
    end;
end;

procedure TMyTest.Test2;
begin
    // ...
end;

Спасибо за ваш быстрый ответ...

1 ответ

Решение

Краткий ответ: нет

Длинный ответ: Контейнер не имеет поддерживаемой возможности отменить регистрацию любых зарегистрированных типов. Это связано с тем, что отмена регистрации чего-либо может вызвать каскадные эффекты для других типов, а также для созданных экземпляров (особенно синглетонов).

Я всегда даю совет не использовать GlobalContainer, а создавать собственный экземпляр TContainer. Затем вы можете просто выбросить экземпляр и использовать новый для вашего следующего теста.

Вы можете посмотреть в наших модульных тестах, как это можно сделать (в Spring.Tests.Container.pas есть класс TContainerTestCase)

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

Другие вопросы по тегам