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)
Кроме контейнера, вы все равно должны избегать использования синглетонов в тестах. Каждый модульный тест должен быть изолированным. Как только глобальное состояние вовлечено, вы можете получить побочные эффекты.