I am writing one of my first unit-tests using spring4d, dunitx and delphi-mocks. (spring4d Release 1.1 - 12.09.2014)
In my testing application, I auto-wire-inject an interface to my System under test class (sut):
IMyInterface = interface [{yes, a GUID here}]
function GetSomething1: TSomething1;
function GetSomething2: TSomething2;
end;
TMyClass = class
private
[Inject]
FMyInterface: IMyInterface;
// ...
end;
Now when I am using unit-tests with mocking, I use the following (very simplyfied) code:
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;
The first test method (Test1) runs fine... but in the second test method(Test2), in the line with GlobalContainer.Build spring4d raises an exception: ERegistrationException('Duplicate service name found: IMyInterface_u.IMyInterface@IMyInterface_u.IMyInterface').
Is there a possibility to unregister aIntfMock, so I can register a new one for every other test-routine?
[Edit] So the solution would be:
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;
Thank you for your fast answer...
Short answer: No
Long answer: The container has no supported possibility to unregister any registered types. This is because unregistering anything could cause cascading effects on other types and also on created instances (especially singletons).
I always give the advice not to use GlobalContainer but create their own instance of TContainer. Then you can just throw the instance away and use a fresh one for your next test.
You can look into our unit tests on how that can be done (there is TContainerTestCase class in Spring.Tests.Container.pas)
Apart from the container you should avoid using singletons in tests anyway. Each unit test should be isolated. As soon as a global state is involved you might get side effects.