Skip to content

Instantly share code, notes, and snippets.

@droyad
Created August 10, 2015 07:35
Show Gist options
  • Save droyad/30097e1f8c02df781ac2 to your computer and use it in GitHub Desktop.
Save droyad/30097e1f8c02df781ac2 to your computer and use it in GitHub Desktop.
Test Autofac registrations by inspection
public abstract class ContainerTestsBase
{
private IContainer _container;
private HashSet<Type> _registeredServices;
readonly Autofac.Core.Activators.Reflection.DefaultConstructorFinder _ctorFinder = new Autofac.Core.Activators.Reflection.DefaultConstructorFinder();
private HashSet<Type> _genericServices;
private HashSet<Type> _registeredConcretions;
[TestFixtureSetUp]
public void FixtureSetUp()
{
if (_container != null)
return;
_container = GetContainer();
_registeredServices = _container.ComponentRegistry
.Registrations
.SelectMany(r => r.Services)
.OfType<IServiceWithType>()
.Select(ts => ts.ServiceType)
.ToHashSet();
_genericServices = (from s in _container.ComponentRegistry.Sources
where s.GetType().Name == "OpenGenericRegistrationSource"
let registrationData = (RegistrationData)s.GetType().GetField("_registrationData", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(s)
from svc in registrationData.Services
where svc is IServiceWithType
select ((IServiceWithType)svc).ServiceType
).ToHashSet();
_registeredConcretions = (from r in _container.ComponentRegistry.Registrations
let type = r.Activator.LimitType
where !type.Namespace.StartsWith("Autofac") &&
!(r.Activator is DelegateActivator) &&
!(r.Activator is ProvidedInstanceActivator)
select type).ToHashSet();
}
protected abstract IContainer GetContainer();
public IEnumerable<TestCaseData> TestCases()
{
FixtureSetUp();
var genericTypes = from s in _container.ComponentRegistry.Sources
where s.GetType().Name == "OpenGenericRegistrationSource"
let activatorData = (ReflectionActivatorData)s.GetType().GetField("_activatorData", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(s)
select activatorData.ImplementationType;
return _registeredConcretions.Union(genericTypes)
.Select(t => new TestCaseData(t).SetName(t.FullName))
.OrderBy(t => t.TestName)
.ToArray();
}
public IEnumerable<TestCaseData> EventHandlerTestCases()
{
return from t in _registeredConcretions
let bt = t.BaseType
where bt != null && bt.IsGenericType
where bt.IsClosedTypeOf(IHandleCompetingEvent<>) ||
bt.IsClosedTypeOf(IHandleMulticastEvent<>) ||
bt.IsClosedTypeOf(IHandle<>) ||
bt.IsClosedTypeOf(IHandleCommand<>)
orderby t.Name
select new TestCaseData(t).SetName(t.FullName);
}
[Test, TestCaseSource(nameof(TestCases))]
public void MustBeAbleToBeResolved(Type type)
{
// Based on https://github.com/autofac/Autofac/blob/master/src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs
var method = _ctorFinder.FindConstructors(type).OrderByDescending(c => c.GetParameters().Length).First();
var missing = method.GetParameters()
.Where(p => !p.ParameterType.IsGenericParameter && !ContainerContainsService(p.ParameterType))
.ToArray();
if (missing.None())
return;
Assert.Fail($"Missing {missing.Select(p => p.ParameterType.Name).ToCsv()} in ctor({method.GetParameters().Select(p => p.ParameterType.Name).ToCsv()})");
}
[Test, TestCaseSource(nameof(EventHandlerTestCases))]
public void EventHandlers(Type type)
{
}
private bool ContainerContainsService(Type parameterType)
{
if (parameterType.IsGenericType)
{
var generic = parameterType.GetGenericTypeDefinition();
if (generic.Name.StartsWith("Func`"))
return ContainerContainsService(parameterType.GenericTypeArguments.Last());
if (generic == typeof (Lazy<>) || generic == typeof (IEnumerable<>))
return ContainerContainsService(parameterType.GenericTypeArguments[0]);
}
if (parameterType.IsArray)
return ContainerContainsService(parameterType.GetElementType());
if (parameterType.BaseType == typeof(MulticastDelegate))
return parameterType.GetMethod("Invoke").GetParameters().Select(p => p.ParameterType).All(ContainerContainsService);
return
parameterType == typeof(string) ||
parameterType.IsEnum ||
parameterType == typeof(Serilog.ILogger) ||
_registeredServices.Contains(parameterType) ||
(parameterType.IsGenericType && _genericServices.Contains(parameterType.GetGenericTypeDefinition()));
}
[TestFixtureTearDown]
public void TearDown()
{
_container.Dispose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment