Search code examples
c#asp.net-coreintegration-testing

How to pass in settings into a class (derived ASP.NET Core Startup.cs) from ConfigureServices in a test


I basically have this code

[Fact]
public async Task Test()
{
   var settings = new MySettings() { Name = "Jhon" }; //<-- use this

   var webHostBuilder = new WebHostBuilder()
    .UseStartup<TestStartup>()
    .ConfigureServices(services =>
    {
        // this is called...
        services.AddSingleton(settings);
    });
       
     //omitted code.. 
 }

And I want to be able to use MySettings in the TestStartup.

Whatever I try just returns Name == null..

I have tried this

public class TestStartup : Startup
{
    public TestStartup(IConfiguration configuration) : base(configuration)
    {
       Configuration = configuration;

       // Name is always != "Jhon"

       // doesn't work
       var mySettings = configuration.Get<MySettings>();

       // doesn't work either
       var mySettings = new MySettings();
       configuration.Bind("MySettings", mySettings);

       // tried various other things that didn't work
    }
}

What am I missing?


Solution

  • You can leverage the MemoryConfigurationProvider:

    using Microsoft.Extensions.Configuration;
    
    var webHostBuilder = new WebHostBuilder()
        .ConfigureAppConfiguration(configurationBuilder => configurationBuilder
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                { "Name", "Jhon" },
                {"Logging:LogLevel:Default", "Warning"}
            }))
        .UseStartup<TestStartup>()
        // ...
    

    and then it can be consumed with any standard pattern for configuration, for example:

    public class TestStartup : Startup
    {
        public TestStartup(IConfiguration configuration) : base(configuration)
        {
           var nameKeyValue = Configuration["Name"];
        }
    }
    

    If you wanted to pass a whole class over it could be done with this helper

    public static class TestHelpers<T> where T : new()
    {
        /// <summary>
        /// Helper class to convert an object of type T to a dictionary.
        /// </summary>
        /// <param name="objectToConvert"></param>
        /// <returns></returns>
        public static Dictionary<string, string?> ConvertObjectToDictionary(T objectToConvert)
        {
            var dictionary = new Dictionary<string, string?>();
    
            foreach (var property in objectToConvert.GetType().GetProperties())
            {
                var value = property.GetValue(objectToConvert);
                dictionary.Add(property.Name, value?.ToString());
            }
    
            return dictionary;
        }
    
        /// <summary>
        /// Helper class to convert a dictionary back to an object of type T.
        /// </summary>
        /// <param name="configDictionary"></param>
        /// <returns></returns>
        public static T ConvertDictionaryToObject(Dictionary<string, string> configDictionary)
        {
            var objectToPopulate = new T();
            var properties = objectToPopulate.GetType().GetProperties();
    
            foreach (var property in properties)
            {
                if (configDictionary.ContainsKey(property.Name))
                {
                    var value = configDictionary[property.Name];
                    if (value != null)
                    {
                        try
                        {
                            property.SetValue(objectToPopulate, Convert.ChangeType(value, property.PropertyType));
                        }
                        catch (Exception ex)
                        {
                            // swallow exception for private properties. This could be logged out
                        }
                    }
                }
            }
    
            return objectToPopulate;
        }
    }
    

    and use it like this

    var settings = new MySettings() { Name = "Jhon" };
    
    // convert the class to dictionary
    var mySettingsDictionary= TestHelpers<MySettings>.ConvertObjectToDictionary(settings);
    
    // and convert it back to class     
    var mySettingsClass= TestHelpers<MySettings>.ConvertDictionaryToObject(mySettingsDictionary);
    
    // and in the TestStartup like this
    var mySettingsClass= TestHelpers<MySettings>.ConvertDictionaryToObject(Configuration.AsEnumerable().ToDictionary(x => x.Key, x => x.Value))