App.config for Xunit - c#

I'm writing some xUnit tests for some helper classes that relies on some configuration settings, usually stored in App.config or Web.config of the executing project.
The config looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="FileNamePattern" value="\\d{8}_\\w{4:20}\.png"/>
<!-- and the likes -->
</appSettings>
</configuration>
I'm running xUnit 1.9 with the GUI runner (xunit.gui.clr4.exe) and xUnit console runner (on the Jenkins CI server). Currently, I can "inject" these configuration values into the test environments by setting the xunit.gui.clr4.exe.config and xunit.console.exe.config files manually); however, this is tedious and error-prone.
I could also mock these configuration settings in a fixture. But using the same fixture across 10 different files is rather repetitive.
Is there a better way to mock these configuration settings with xUnit, such as providing a App.config file for the test project?

If your code assumes they are in the app.config, then xUnit.net supports having them wired up in there by providing one (typically when the tests are in a DLL file, this means you get a AssemblyName.dll.config file in the project outputs which the runner loads as the settings if it exists at load time).
Obviously no harm to use the DI principle to remove such dependencies in the first place, but I'd say don't go messing with code before you actually get it under test first.
To keep it DRY, put the app.config in a central place and add it as a link (via the arrow on the Open button in the dialog). (Yes, there's lots not to like about that - use only if you feel its the least evil approach.)
One thing to look out for is that changes don't get reloaded in the GUI runner unless you ask for the assembly to be reloaded.

From perspective more complex projects & team work, I recommend:
separate .config file for xUnit project (it takes advantage of independent configuration & logging for running tests)
set-up Dependency Injection together with .config reading for xUnit project alone
Our team is using this pattern of xUnit init & dispose:
public class MyTest : IDisposable
{
public IServiceProvider Services { get; private set; }
public MyProjectOptions Options { get; private set; }
public Logger Logger { get; private set; }
private void Configure()
{
// appsettings.workspace.json for custom developer configuration
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.workspace.json", optional: true)
.Build();
Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.LiterateConsole()
.WriteTo.RollingFile("logs/{Date}-log.txt")
.CreateLogger();
Options = configuration.GetSection("MyProject").Get<MyProjectOptions>();
var services = new ServiceCollection();
services.AddSingleton<ILogger>(s => Logger);
// other DI logic and initializations ...
//services.AddTransient(x => ...);
Services = services.BuildServiceProvider();
}
public MyTest()
{
Configure();
// ... initialize data in the test database ...
var data = Services.GetService<TestDataService>();
data.Clean();
data.SeedData();
}
public void Dispose()
{
// ... clean-up data in the test database ...
var data = Services.GetService<TestDataService>();
data.Clean();
}
}

The solution for me was to name the config file testhost.dll.config, add it to the solution, and set its Copy to Output Directory setting to Copy always.
It has to be named testhost.dll.config because that's how the xUnit component that invokes my tests-project is named. It can be determined as follows:
string invoker = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
For my setup, invoker turned out to be testhost.dll.

Related

How to load appsettings.Development.json from project within XUnit tests

My tests use HttpClient. So I have built a helper method init the client in the constructor of all of my test files.
I initially tried using AppContext.BaseDirectory to get the base directory for the project that I was trying to test. The problem is that its not picking up the correct project name so its loading the appsettings.Development.json from a different project causing the tests to fail. I ended up hard coding the project dir.
I cant figure out how I can force it to load the appsettings.Development.json from the project I want to test.
public static HttpClient GetHttpClient()
{
// projectDir one, loads tests project.
// var projectDir = AppContext.BaseDirectory;
var projectDir = #"C:\Work\Src\Application\bin\Debug\net5.0";
var server = new TestServer(new WebHostBuilder()
.UseEnvironment("Development")
.UseConfiguration(new ConfigurationBuilder()
.SetBasePath(projectDir)
.AddJsonFile("appsettings.Development.json")
.Build()
)
.UseStartup<Startup>()
);
return server.CreateClient();
}
The main issue being that it cant load the settings for RabbitMQ which come from the appsettings.Development.json file.
// Load RabbitMQ config.
var serviceClientSettingsConfig = Configuration.GetSection("RabbitMq");
services.Configure<RabbitMqConfiguration>(serviceClientSettingsConfig);
Note: I plan to mock this latter but for now my client needs to see end to end tests working.

How can I keep my (.NET Core) Dependency Injection configuration maintainable?

I am using Microsoft.Extensions.DependencyInjection for Dependency Injection in .NET Core Console Application.
public class Program
{
public IConfiguration Configuration { get; }
public static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
Startup startup = new Startup();
startup.ConfigureServices(services);
IServiceProvider serviceProvider = services.BuildServiceProvider();
var etlService = serviceProvider.GetService<IETLService>();
}
}
public class Startup
{
IConfigurationRoot Configuration { get; }
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
var sqlServerConnectionString = Configuration.GetConnectionString("SqlServerConnection");
services.AddDbContext<ETLSqlContext>(options =>
options.UseSqlServer(sqlServerConnectionString), ServiceLifetime.Scoped);
services.AddSingleton(Configuration);
services.AddTransient<ISqlRepository, SqlRepository>();
services.AddTransient<IAzureSqlRepository, AzureSqlRepository>();
services.AddTransient<IExtractService, ExtractService>();
services.AddTransient<ILoadService, LoadService>();
}
}
public class ExtractService : IExtractService
{
public ISqlRepository SqlRepository { get; set; }
public IAzureSqlRepository AzureSqlRepository { get; set; }
public ExtractService(ISqlRepository sqlRepository, IAzureSqlRepository azureSqlRepository)
{
SqlRepository = sqlRepository;
AzureSqlRepository = azureSqlRepository;
}
}
As solution grows there will be more services for example 50+ and each service will require registering its Interface and Implementation class in Startup.cs for Dependency Injection. I need to know is there any better way to implement Dependency Injection which does not require manually adding new service Interface and Implementation class in registration code
In chapter 12 of Dependency Injection, Principles, Practices, and Patterns, Mark Seemann and I describe that there are multiple configuration options when working with a DI Container, namely:
Configuration files–Mapping are specified in configuration files (typically in XML or JSON format)
Configuration as Code–Code explicitly determines mappings
Auto-Registration–Rules are used to locate suitable components using reflection and to build the mappings.
You are currently applying Configuration as Code. With Auto-Registration, however, you apply Convention over Configuration to register your application components using reflection, based on a specified convention.
In section 12.3 we describe in detail when you should use a DI Container, and how you should use it. In summary we state that:
The use of Convention over Configuration using Auto-Registration can minimize the amount of maintenance on the Composition Root to almost zero.
We, therefore, advice:
a Composition Root should either be focused around Pure DI with, perhaps a few late-bound types, or around Auto-Registration with, optionally, a limited amount of Configuration as Code and configuration files. A Composition Root that focuses around Configuration as Code is pointless and should therefore be avoided.
In chapters 6 and 10 of the book, we also describe the kinds of designs you can use that, among other things, maximize convention over configuration and, with it, minimizes the amount of maintenance on your DI configuration.
I've done this by adding a class to each of my projects "PluginConfiguration", which passes in the IServiceCollection, and registers its individual services in the constructor.. Then just call each "Plugin" in the ServicesCollection in my main project. It abstracts out the config into a class thats relevant to the individual project and keeps the main config clean with just a single line of code for each additional project

C#: Run each unit test with different configuration file

I'm currently developing a plugin to use in any application, and the configuration of the plugin is done through the usage of either a web.config or an app.config file.
Now, I want to unit test my plugin. I want to test the different behaviour in various conditions:
For exmaple:
Only 1 single item should be marked as 'default'.
A collection of providers can be registered in the plugin, however the name must be unique, otherwise, a specific exception is thrown.
My question:
I have a unit test project and I want to test each method with another application configuration file, is this possible?
I've tried reading the file from ExeConfiguration, but I don't get it to work.
Thanks for the feedback.
Kr,
This is what we do (same as #THG) explained in his answer.
public interface IConfiguration
{
string SettingA { get; }
string SettingB { get; }
}
public class Configuration : IConfiguration
{
public string SettingA
{
get
{
return ConfigurationManager.AppSettings["SettingA"];
}
}
public string SettingB
{
get
{
return ConfigurationManager.AppSettings["SettingB"];
}
}
}
Then in your test
var config = MockRepository.GenerateStub<IConfiguration>();
config.Stub(x => x.SettingA).Return("Fred");
My recommendation is to mock the integration with the config file instead of using it directly. This approach offers much more flexibility and removes the need to worry about multiple config files.
You can either use a mocking framework or create you own layer of abstraction on top of the code that fetches the config values.

Unit test SerializableConfigurationSection programmatically

I'm using strongly typed configuration sections in my project and want to unit test a particular area which throws an exception when a setting isn't set-up correctly.
A snippet of the configuration class:
public class EmailSettings : SerializableConfigurationSection, IEmailSettings
{
[ConfigurationProperty("from", IsRequired = true)]
public string From
{
get
{
...
}
set
{
...
}
}
...
}
Sample test method:
[TestMethod]
public void something_describing_this_test()
{
EmailSettings settings = new EmailSettings();
settings.From;
}
I expect that SerializableConfigurationSection and its inners are looking for a web.config (or similar) to read xml config from.
How can I get in the middle and 'mock' the configuration to enable me to pipe custom values to test for certain conditions? This question (using ConfigurationManager methods) appears to do it via a physical config file in the assembly - is this the only way or can I get in there programmatically?
You could generate one in your test fixture setup, and then load it like described in the answer to this question:
Loading custom configuration files

Using StructureMap with unit tests

I'm using StructureMap in a web project for DI IOC. It works perfect, but I don't have a clue how to write unit tests with StructureMap.
Should I do this in AssemblyInitialize start Configuration of StructureMap like in global.asax except for datacontext not to use live LinqToSqlDataContext but some memory data like this:
[AssemblyInitialize]
public static void Start()
{
ObjectFactory.Configure(x =>
{
x.For<IDataContext>().HttpContextScoped().Use<MemoryDataContext>()
.Ctor<string>("connectionString")
.Is(ConfigurationManager.ConnectionStrings["DEVConnection"].ConnectionString);
x.For<IDepartamentRepository>().Use<DepartamentDB>();
x.For<IDevelopmentProcess>().Use<DevelopmentProcesses>().OnCreation(c => c.User = Current.CurrentUser);
x.For<IActivityProcess>().Use<ActivitiesProcess>().OnCreation(c=> c.User = Current.CurrentUser);
x.For<IDevDeveloperRepository>().Use<DevDeveloperDB>();
x.For<IDevelopmentRepository>().Use<DevelopmentDB>();
x.For<IActivityRepository>().Use<ActivityDB>();
x.For<IActivityTypeRepository>().Use<ActivityTypeDB>();
x.For<IDevUserRepository>().Use<DevUsersDB>();
x.For<IAttachmentRepository>().Use<AttachmentDB>();
}
);
}
and then use ObjectFactory.GetInstance() testing or how do I do this?
You shouldn't need to use a DI Container in unit tests at all.
A container is something you use to wire components together, but a unit test is a test of each component in isolation.
I agree with Mark. Testability is one of the primary reasons you are likely using a container in the first place.
There are times where creating an integration test for your container setup might be a good idea. For example if you have any behavior in your container configuration you will want to create tests for that behavior. In you container config you set IDataContext's connection string via the configuration manager.
The following code is similar to what I do to test such a setup. Notice I avoid ObjectFactory (static singleton objects have their own problems) and wrap my container setup in a bootstrapper helper class:
[Test]
public void connection_string_should_come_from_application_configuration()
{
var container = new ContainerBootstraper().Container;
var connectionString = container.GetInstance<IDataContext>().ConnectionString
connectionString.ShouldEqual("test project application configuration connection string");
}

Categories