Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integration test with IOptions<> in .NET Core

I pass IOption<T> to my CommandBus so I can get the settings from my ServiceBusSetting class. I want to do an integration test of my Bus. I do not want to resolve it just use new QueueCommandBus and need to pass IOptions to it.

var services = new ServiceCollection().AddOptions();
        services.Configure<ServiceBusAppSettings>(Configuration.GetSection("ServiceBus"));
        var options = services.BuildServiceProvider().GetService<IOptions<ServiceBusAppSettings>>();

        ////Act
        var commandBus = new QueueCommandBus(options);

This works fine, but feels very complex code to get the IOptions<T> from my appsetting.json in my test project.

Any clue if this is the only way or is there a better way?

like image 574
Jnormen Avatar asked Feb 26 '16 10:02

Jnormen


People also ask

What is the use of IOptions in .NET core?

IOptionsMonitor is a Singleton service that retrieves current option values at any time, which is especially useful in singleton dependencies. IOptionsSnapshot is a Scoped service and provides a snapshot of the options at the time the IOptionsSnapshot<T> object is constructed.

What is integration test in .NET core?

Integration tests ensure that an app's components function correctly at a level that includes the app's supporting infrastructure, such as the database, file system, and network. ASP.NET Core supports integration tests using a unit test framework with a test web host and an in-memory test server.

How do I pass IOptions?

To achieve the set up of having an AppSettings model being passed around the application, you need to create the app settings in your actual appsettings. json, have it match the model, and configure it within the start up. After doing so, you can pass in an IOptions<T> to a class and it will resolve correctly.


1 Answers

You don't need to create the ServiceCollection or IServiceProvider. The IConfiguration interface has a Bind() method, or from .NET Core 1.1 onwards, Get<T> which you can use to get the strongly-typed object directly:

var config = Configuration.GetSection("ServiceBus");

// .NET Core 1.0
var options = new ServiceBusAppSettings();
config.Bind(options);

// .NET Core 1.1
var options = config.Get<ServiceBusAppSettings>();

I like to add these as static methods to my AppSettings strongly-typed object, to make it convenient to load them from JSON in both my web app and from unit tests.

AppSettings.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;

namespace My.Namespace
{
    public class AppSettings
    {
        public class ServiceBusAppSettings
        {
            public string Setting1;
            public int Setting2;
        }

        public class ApiSettings
        {
            public bool FormatJson { get; set; }
        }

        public class MySqlSettings
        {
            public string User { get; set; }
            public string Password { get; set; }
            public string Host { get; set; }
            public string Database { get; set; }
            public int Port { get; set; } = 3306;

            public string GetConnectionString()
            {
                return $"Server={Host};Database={Database};Port={Port};Uid={User};Pwd={Password}";
            }

        }

        public ServiceBusAppSettings ServiceBus { get; set; } = new ServiceBusAppSettings();
        public ApiSettings Api { get; set; } = new ApiSettings();
        public MySqlSettings MySql { get; set; } = new MySqlSettings();

        // Static load helper methods. These could also be moved to a factory class.
        public static IConfigurationRoot GetConfiguration(string dir)
        {
            return GetConfiguration(dir, null);
        }

        public static IConfigurationRoot GetConfiguration(string dir, string environmentName)
        {
            if (string.IsNullOrEmpty(environmentName))
                environmentName = "Development";

            var builder = new ConfigurationBuilder()
                .SetBasePath(dir)
                .AddJsonFile("appsettings.json", true, true)
                .AddJsonFile($"appsettings.{environmentName}.json", true)
                .AddEnvironmentVariables();

            return builder.Build();
        }

        public static AppSettings GetSettings(string dir)
        {
            return GetSettings(dir, null);
        }

        public static AppSettings GetSettings(string dir, string environmentName)
        {
            var config = GetConfiguration(dir, environmentName);
            return GetSettings(config);
        }

        public static AppSettings GetSettings(IConfiguration config)
        {
            return config.Get<AppSettings>();
        }
    }
}

ASP.NET Core Startup.cs: (Getting the strongly-typed settings object is often helpful at this stage, when configuring the other services...)

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        Configuration = AppSettings.GetConfiguration(env.ContentRootPath, env.EnvironmentName);
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Configure the service collection.
        services.AddOptions();
        services.Configure<AppSettings>(Configuration);

        // It can also be handy to get the AppSettings object here.
        var settings = AppSettings.GetSettings(Configuration);

        // Add framework services.
        services.AddMvc()
            .AddJsonOptions(options =>
            {
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                // Pretty-print JSON in Development
                options.SerializerSettings.Formatting = settings.Api.FormatJson ? Formatting.Indented : Formatting.None;
            });

        // Store DB connection info in AppSettings too...
        var conn = settings.MySql.GetConnectionString();
        services.AddDbContext<MyDbContext>(opt => opt.UseMySql(conn));
    }
}

In Test Class:

var testDir = AppContext.BaseDirectory;
var settings = AppSettings.GetSettings(testDir, "Test");

//Act
var commandBus = new QueueCommandBus(settings);
like image 64
Tobias J Avatar answered Oct 12 '22 23:10

Tobias J