Skip to content

Instantly share code, notes, and snippets.

@jfoshee
Created May 26, 2021 19:19
Show Gist options
  • Save jfoshee/72af34a186748ef3c5fa52507c5aad3b to your computer and use it in GitHub Desktop.
Save jfoshee/72af34a186748ef3c5fa52507c5aad3b to your computer and use it in GitHub Desktop.
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Xunit.Sdk;
namespace Example.Testing
{
/// <summary>
/// Provides way to leverage standard <see cref="IServiceCollection"/> Configuration
/// to inject services to unit tests.
/// </summary>
public abstract class ServiceProviderDataAttributeBase : DataAttribute
{
protected abstract void ConfigureServices(IServiceCollection services);
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
var parameters = GetServices(testMethod);
return new[] { parameters };
}
private object[] GetServices(MethodBase method)
{
var serviceProvider = GetServiceProviderInstance();
return method.GetParameters()
.Select(p => GetService(p, serviceProvider))
.ToArray();
}
private object GetService(ParameterInfo p, IServiceProvider serviceProvider)
{
return serviceProvider.GetService(p.ParameterType)
?? GetTestService(p)
?? throw new Exception($"'{p.ParameterType.Name} {p.Name}': Unable to resolve a test service for the parameter.");
}
private object GetTestService(ParameterInfo p)
{
var type = p.ParameterType;
// If it has a default constructor just construct and return
if (type.CanDefaultConstruct())
return Activator.CreateInstance(type);
// Otherwise let's try to recurse getting required parameters for a constructor
if (!type.IsAbstract && type.GetConstructors().Any())
{
// HACK: Just try the first constructor...
var constructor = type.GetConstructors().First();
var constructorArguments = GetServices(constructor); // Recursion
return constructor.Invoke(constructorArguments);
}
return null;
}
private IServiceProvider GetServiceProviderInstance()
{
return serviceProviderInstance ??= BuildServiceProvider();
}
private IServiceProvider serviceProviderInstance;
private IServiceProvider BuildServiceProvider()
{
//var configuration = LoadConfiguration();
//var startup = new Startup(configuration);
var services = new ServiceCollection();
ConfigureServices(services);
return services.BuildServiceProvider();
}
}
}
@jfoshee
Copy link
Author

jfoshee commented May 26, 2021

Requires Type Extensions in https://gist.github.com/jfoshee/65f00692e39845e247e1ba315b02d0d4 for type.CanDefaultConstruct()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment