That worked, full test below, with lifetime Scoped i could not test just against LifestyleType (that would have worked if my interface was a Singleton) so i had to test the type of CustomLifestyle.
Example below:
[Fact]
public void Should_have_lifetime_scoped()
{
var container = IoC.Resolve<IWindsorContainer>() as WindsorContainer;
container.ShouldNotBeNull();
var customerService = container.Kernel.GetAssignableHandlers(typeof(ICustomerService));
customerService.ShouldNotBeNull();
var componentModel = customerService[0].ComponentModel;
componentModel.LifestyleType.ShouldBe(LifestyleType.Custom);
componentModel.CustomLifestyle.BaseType.Name.ShouldBe("ScopedLifestyleManager");
}
This will only be a part of the solution, you also need to identify the service that is injected as ctor parameters to ensure that not an singleton is injecting a scoped service.
Good point, here is the updated test, implemented a recursive check on constructors to verify that a singleton is not injecting a non-singleton (I have not implemented support for transient).
using System;
using System.Collections.Generic;
using Castle.Core;
using Castle.Windsor;
using Litium.Accelerator.Services;
using Litium.Runtime.DependencyInjection;
using Shouldly;
using Xunit;
namespace Litium.Accelerator.Tests.Common
{
public class DependencyInjectionTests : LitiumApplicationTestBase
{
[Theory]
[InlineData(typeof(ICustomerService), DependencyLifetime.Scoped)]
[InlineData(typeof(IWorkshopService), DependencyLifetime.Scoped)]
public void Component_should_be_registered_with_lifetime(Type registeredComponentType, DependencyLifetime dependencyLifetime)
{
var container = IoC.Resolve<IWindsorContainer>() as WindsorContainer;
container.ShouldNotBeNull();
VerifyTypeLifetime(container, registeredComponentType, dependencyLifetime, new List<string>());
}
private void VerifyTypeLifetime(WindsorContainer container, Type registeredComponentType, DependencyLifetime dependencyLifetime, List<string> callPath, string customMessage = null)
{
var registeredComponent = container.Kernel.GetAssignableHandlers(registeredComponentType);
registeredComponent.ShouldNotBeNull($"No registered component found for '{registeredComponent}'");
var componentModel = registeredComponent[0].ComponentModel;
callPath.Add(componentModel.Name);
var message = $"{customMessage} Path:'{string.Join(" > ", callPath)}'";
if (dependencyLifetime == DependencyLifetime.Scoped)
{
componentModel.LifestyleType.ShouldBe(LifestyleType.Custom, message);
componentModel.CustomLifestyle.BaseType.Name.ShouldBe("ScopedLifestyleManager", message);
}
else if (dependencyLifetime == DependencyLifetime.Singleton)
{
componentModel.LifestyleType.ShouldBe(LifestyleType.Singleton, message);
// If the current service is a singleton, verify that it is not injecting a scoped service!
var constructors = componentModel.Implementation.GetConstructors();
foreach (var constructor in constructors)
{
var constructorParams = constructor.GetParameters();
foreach (var constructorParam in constructorParams)
{
var lifetimeWarnMsg = "A SINGLETON SERVICE SHOULD NOT INJECT A SCOPED OR TRANSIENT SERVICE";
VerifyTypeLifetime(container, constructorParam.ParameterType, DependencyLifetime.Singleton, callPath, lifetimeWarnMsg);
}
}
}
else
{
true.ShouldBeFalse($"{message} : Lifetime {dependencyLifetime} is not yet supported in test");
}
}
}
}
Using a transient service in a singleton or scoped service is not a problem, so that warning should probably be removed from the recursive check (refer to lifetimeWarnMsg).
As long as the injected service isn’t stored in the class any service can be injected, I’ll implement support for exceptions to the rule if those issues come up.