All,
I am using Unit test framework to write my tests in .net. I wrote some unit tests that all access a configuration object (via AppConfig.Current) that is internally a class static.
In the startup I initialize this configuration object and use it immediately in the following line like this:
[AssemblyInitialize]
public static void AssemblyInitialize(TestContext context)
{
var appCfg = AppConfig.Current;
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "conf/app.settings.xml");
appCfg.Load(path);
var _ = AccountingContext.Current; // <= AccountingContext references the configuration object via "AppConfig.Current" to read some configuration values and initialize the accounting context but I receive a different instance (ONLY in release mode) ....
}
After loading configuration, next line uses this configuration object (AccountingContext.Current) to initialize itself from the configuration object.
The issue is, the tests run fine in Debug mode but in Release mode when I reference the configuration object I get a different object (I know this for a fact because the configuration is not loaded).
So what is the deal with using static reference in unit tests in release mode???
UPDATE: SELF ANSWERED
The issued lied in the "AccountingContext" class.
This class is also a static and it initialized itself like this:
public static AccountingContext Current { get; set; } = new AccountingContext();
public AccountingContext()
{
CompanyAccessContext = _CreateCompanyAccessContext();
}
Changing this intialization to using a static ctor resolved this issue and now everything works:
public static AccountingContext Current { get; set; }
static AccountingContext()
{
Current = new AccountingContext();
}
public AccountingContext()
{
CompanyAccessContext = _CreateCompanyAccessContext();
}
Looks like optimizations were made which changed the actual flow of initialization of AccountingContext. That's a bit unwanted, not sure if this is what everyone expects.
I also found this good resource:
Static member variable not being initialized in Release - Compiler/clr bug?
Cause of this issue was not using static ctor which guarantees order of initialization. For more details, see updated comments above.
Related
I'm currently writing a suite of integration tests for a mature ASP.NET application.
As part of the application's supporting services, it uses an Azure blob storage container which I need to make sure is accessible and existing prior to running the tests. I want to add what effectively amounts to a check that the configured Azure blob container for the application (be it on the local emulator or Azure itself when running in CI) is up and ready to handle the requests made by the test suite. A large amount of the tests will straight up fail if the backend is inaccessible and they take several minutes to fail as the Azure library waits through several timeouts.
When throwing or asserting in a collection fixture, it doesn't seem to actually prevent any test in the collection from running nor does the fixture exception appear in the resulting logs.
Is there a way to prevent tests from running if any of their associated fixtures throw during instantiation?
My current code is as follows:
The fixture
using Azure.Storage.Blobs;
using Newtonsoft.Json.Linq;
using System.IO;
using Xunit;
namespace product.test.integration
{
/// <summary>
/// This fixture ensures the configured azure blob container used by the FileStorage system is accessible.
/// A concern mostly in local environments where the Azurite emulator may not be running, but also can alert to a misconfigured testing environment.
/// </summary>
public class FileStorageFixture
{
public FileStorageFixture()
{
//get the configuration
string appdataLocation = Path.Combine(Directory.GetCurrentDirectory(), "testappsettings.json");
string appdataContent = File.ReadAllText(appdataLocation);
var json = JObject.Parse(appdataContent);
string connectionString = json["Settings"]["FileStorage"]["AzureStorageKey"].Value<string>();
string containerName = json["Settings"]["FileStorage"]["ContainerName"].Value<string>();
//check if the container exists
var container = new BlobContainerClient(connectionString, containerName);
try
{
Assert.True(container.Exists()); //this line will throw if a timeout occurs
} catch
{
throw new System.Exception("Configured FileStorage backend is not accessible!");
}
}
}
}
The base test class (IntegrationTest) which all tests extend, and the collection that contains them:
//...
[CollectionDefinition("Integration Tests", DisableParallelization = true)]
public class IntegrationTestCollection :
//...
ICollectionFixture<FileStorageFixture> { }
[Collection("Integration Tests")]
public abstract class IntegrationTest : IClassFixture<WebApplicationFactory<product.api.Startup>>
{
//...
One way to accomplish what you desire is to have the fixture set a flag if its initialization was not successful and query the flag in the constructor of your base test class.
In FileStorageFixture add the flag as a property
public bool initOK { get; private set; } = false;
and set the property depending on whether the initialization was successful
try
{
Assert.True(container.Exists()); //this line will throw if a timeout occurs
initOK = true;
}
catch
{
initOK = false;
}
It would be even more reliable if you included the rest of the code in the FileStorageFixture constructor inside the try, since an exception anywhere there would also bring your tests down.
I assume the reference to the fixture is being provided as a parameter to the constructor of IntegrationTest, so you should throw an exception there if the fixture wasn't initialized properly:
public IntegrationTest(FileStorageFixture fixture)
{
Assert.True(fixture.initOK, "Configured FileStorage backend is not accessible!");
// ... and do the rest of the constructor stuff
}
Without the IntegrationTest object, none of its tests will run.
How can I create a global variable in an ASP.NET Core Web API application? In ASP.NET MVC, I could do it like:
Application["<variableName>"] = <value>
I tried the same in my web API application, but was unable to find an equivalent for this. I saw some solutions which suggested me to store the data appsettings.json, but since the data I want to store in the global variable is not static, I cannot use that setup. I need to set different data during runtime. How can I do that? Please help me.
We could use Singleton_pattern , creating an Application static object in .net core.
we can also try to use Depend Injection, register a singleton object as below in your code.
Writing a property ConcurrentDictionary in ApplicationInstance class.
public class ApplicationInstance {
public ConcurrentDictionary<string,object> Application { get; } = new ConcurrentDictionary<string, object>();
}
public void ConfigureServices(IServiceCollection services){
services.AddSingleton<ApplicationInstance>();
}
Then we might use this object
public class HomeController : ControllerBase
{
private readonly ApplicationInstance _application;
public HomeController(ApplicationInstance application)
{
this._application = application;
}
//use _application["test"] instance in your code
}
I would use ConcurrentDictionary to help us avoid racing-condition which will be happened on multiple-thread accessing the same object concurrently.
somewhere in project
public static class Config{
public static Dictionary<string,string> Application = new Dictionary<string,string>();
}
elsewhere
Config.Application["froop"] = "noodle";
of course you will have to deal with race conditions, etc
First of all thank you all for finding some time to help me. I took #pm100's solution and made a slight modification to suit my purpose. I could not use it directly as I am using SonarLint, and it had some issues, so I used a KeyValuePair instead of Dictionary like this:
public static class GlobalData
{
public static KeyValuePair<string,object> Application { get; set; }
}
and I used it in my code like:
string value = GlobalData.Application.Value
and luckily it works without any issue.
I'm trying to test business logic in queries in services. So I don't want my tests to have real access to the database, because they are unit tests, not integration tests.
So I've made a simple example of my context and how I'm trying to shim it.
I have an entity
public class SomeEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
and a service
public class Service
{
public int CountSomeEntites()
{
using (var ctx = new Realcontext())
{
int result = ctx.SomeEntities.Count();
return result;
}
}
}
And this is the real context
public partial class Realcontext : DbContext
{
public virtual DbSet<SomeEntity> SomeEntities { get; set; }
public Realcontext() : base("name=Realcontext")
{
InitializeContext();
}
partial void InitializeContext();
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
So I've tried to create a fake context and I detourned the constructor of the real context in my test method
This is the fake context
public class FakeContext : DbContext
{
public DbSet<SomeEntity> SomeEntities { get; set; }
public FakeContext()
{
}
}
And finally the test class
[TestClass]
public class ServiceTests
{
[TestMethod]
public void CountEmployee_ShoulReturnCorrectResult()
{
using (ShimsContext.Create())
{
ShimRealcontext.Constructor = context => GenerateFakeContext();
ShimDbContext.AllInstances.Dispose = () => DummyDispose();
Service service = new Service();
int result = service.CountSomeEntites();
Assert.AreEqual(result, 2);
}
}
private FakeContext GenerateFakeContext()
{
FakeContext fakeContext = new FakeContext();
fakeContext.SomeEntities.AddRange(new[]
{
new SomeEntity {Id = 1, Name = "entity1"},
new SomeEntity {Id = 2, Name = "entity2"}
});
return fakeContext;
}
}
When I run the test, the RealContext constructor is returned properly, a FakeContext is built in the GenerateFakeContext() method, it contains 2 SomeEntities and it is returned, but right after, in the service, the property SomeEntities of the variable ctx equals to null.
Is it because my variable ctx is declared as a new RealContext()? But calling the constructor of RealContext returns a FakeContext(), so isn't the variable supposed to be of type FakeContext?
Am I doing something wrong? Or is there any other way to test the service without accessing the real database?
I had the simlair situation and I solved it with build configuration and conditional compilation. It's not the best solution, but it worked for me and solved the problem. Here is the receipt:
1. Create DataContext interface
First you need to create an interface which will be implemented by both context classe you going to use. Let it be named just 'IMyDataContext'. Inside it you need to describe all DbSets you need to have access to.
public interaface IMyDataContext
{
DbSet<SomeEntity> SomeEntities { get; set; }
}
And both your context classes need to impelemt it:
public partial class RealDataContext : DataContext, IMyDataContext
{
DbSet<SomeEntity> SomeEntities { get; set; }
/* Contructor, Initialization code, etc... */
}
public class FakeDataContext : DataContext, IMyDataContext
{
DbSet<SomeEntity> SomeEntities { get; set; }
/* Mocking, test doubles, etc... */
}
By the way you can even make properies read-only at interface level.
2. Add 'Test' build configuration
Here you can find how to add new build configuration. I named my configuratin 'Test'. After new configuration is created, go to your DAL project properties, Build section on the left pane. In the 'Configuration' drop-down select the configuration you've just created and in input 'Conditional compilation symbols' type 'TEST'.
3. Incapsulate context injection
To be clear, my approach is still method/property based DI solution =)
So now we need to implement some injection code. For simplicity you can add it right into your service or extract into another class if you need more abstraction. The main idea is to use conditional compilation direcitves instead of IoC framework.
public class Service
{
// Injector method
private IMyDataContext GetContext() {
// Here is the main code
#if TEST // <-- In 'Test' configuration
// we will use fake context
return new FakeDataContext();
#else
// in any other case
// we will use real context
return new RealDataContext();
#endif
}
public int CountSomeEntites()
{
// the service works with interface and does know nothing
// about the implementation
using (IMyDataContext ctx = GetContext())
{
int result = ctx.SomeEntities.Count();
return result;
}
}
}
Limitations
The described approach solves the the problem you described, but it has a limitation: as IoC allows you switch contexts dynamically at runtime, conditional complation requires you to recompile the solution.
In my case it's not a problem - my code isn't covered by tests for 100% and I don't run them on each build. Usually I run tests only before commiting the code, so it's very easy to switch the build configuration in VS, run tests, make sure that nothing was broke and then return to debug mode. In release mode you don't need to run test either. Even if you need - you can craete "Release build test mode" configuration and continue to use the same solution.
Another problem is if you have continuos integration - you need to make additional setup to your build server. Here you have two ways:
Setup two build definitions: one for release and one for tests. If your server is setup to automatic release you need to be careful because test fails will be shown in the second one while the first is deployed.
Set complex build definition which builds your code in Test configuration for the first time, runs test and if they are OK - then recompiles the code in target configuration and prepare to deploy.
Thus, as any solution this one is yet another compromise between simplisity and flexibility.
UPDATE
After some time I understand that the way I described above is very heavy. I mean - build configurations. In case of only two IDataContext implementations: 'Core' and 'Fake' you can simply use bool paramenter and simple if/else branches instead of compilation directives #if/#else/#endif and all the head ache configuring your build server.
If you have more than two implementations - you can use enum and switch block. A probem here is to define what you will return in default case or if value is out of enum's range.
But the main benefit of such approach is that you can be no longer bound to compilation time. Injector parameter could be changed at any time, for example using web.config and ConfigurationManager. Using it you could event switch your data context at run time.
I am using MSTest to test an application. The test requires certain specific values, which are not normally present, to appear in the application config file.
So I need to substitute a well-known config file containing the values, at test run time, so that System.Configuration.ConfigurationManager points at the right file. (ie I am faking the real config file by substituting another one that I made earlier)
I can do all that, except that by the time my test executes, System.Configuration.ConfigurationManager has already read the config file, so that the new values are ignored.
Example code:
static TemporaryConfigFile config;
[ClassInitialize]
public static void ClassInitialise(TestContext testContext)
{
string sourceResource = "Intra_Matrix_Scheduler_Tests.Resources.test.config";
string tempConfigFileName = "test.config";
config = TemporaryConfigFile.CreateFromEmbeddedResource(Assembly.GetExecutingAssembly(), sourceResource, tempConfigFileName);
}
[ClassCleanup]
public static void ClassCleanUp()
{
config.Dispose();
}
(the above code creates a new config file with known test values, and points AppDomain.CurrentDomain("APP_CONFIG_FILE") at it. In the production code this technique of rerouting to another config file works perfectly if done at the start of the application)
The problem is that the following production line, when exercised by a test, does not retrieve the desired test values:
var dict = (System.Collections.Specialized.NameValueCollection)System.Configuration.ConfigurationManager.GetSection("ScheduledTasks");
and the reason is clearly that although the production code line and test code are by now pointing at the correct config file, the production config file has already been loaded into memory so the test config file is effectively ignored.
So the question is: how can System.Configuration.ConfigurationManager be forced to re-read the config file, or how else can the config file be faked? Alternatively how can I directly modify the in-memory config file for the duration of the test? (AFAIK I can't use dependency injection and MOQ to mock it, because System.Configuration.ConfigurationManager is static)
TIA
I suggest you to test classes in isolation from other real classes (like ConfigurationManager) and especially in isolation from environment (files, network, databases etc), because your tests could fail for some external reason not related to code you are testing (file may not exist, wrong database connection, etc). It's easy to do if you'll create your own non-static configuration manager, which will delegate all work to ConfigurationManager:
public class ConfigurationManagerWrapper : IConfigurationProvider
{
public NameValueCollection GetScheduledTasksSettings()
{
return (NameValueCollection)ConfigurationManager
.GetSection("ScheduledTasks");
}
}
Then make your sut (class under test) depend on ICoolConfigurationProvider abstraction which is easy to mock (consider also to return something more business specific than name-value collection):
public interface IConfigurationProvider
{
NameValueCollection GetScheduledTasksSettings();
}
And sut looks like:
public class SUT
{
private IConfigurationProvider _configProvider;
public SUT(IConfigurationProvider configProvider)
{
_configProvider = configProvider;
}
public void Exercise()
{
var dict = _configProvider.GetScheduledTasksSettings();
// ...
}
}
Now you can easily provide any values for your tests:
[TestMethod]
public void ShouldDoSomething()
{
var configMock = new Mock<IConfigurationProvider>();
configMock.Setup(c => c.GetScheduledTasksSettings())
.Returns(new NameValueCollection {{ "foo", "bar" }});
var sut = new SUT(configMock.Object); // inject configuration provider
sut.Exercise();
// Assertions
}
Is it possible to use automapper in a console application?
Its Getting Started Page suggests the bootstrapper class be called from Application start up, but there are no further details about a class to add and call from Main().
How do I go about using this in a simple console app?
You can initialize Automapper in the console startup, there's no limitations; the Application_start is the startup place for a web program in .net/iis, ie code that is called only once. Any configuration that you must call at the start of a web project goes in this method.
edit for comment: if you don't want to create your mappings on the fly, but would rather have a place to initialize all your mappings, just create a function called InitializeAutomapper and make the Mapper.Configure<X, Y> calls in here. Then in your Main() method, just call the function. There are lots of ways to handle configuration, but this is the simpler way to handle it.
code sample
class Program
{
static void Main(string[] args)
{
// the app is starting here
InitializeAutomapper();
// we're configured, let's go!
DoStuff();
}
static void InitializeAutomapper()
{
AutoMapper.Mapper.CreateMap<TypeA, TypeB>();
AutoMapper.Mapper.CreateMap<TypeC, TypeD>();
AutoMapper.Mapper.CreateMap<TypeE, TypeF>();
}
}
I know that this is an old question, but if you found this I want to add an update: Automaper does not allow static initialization anymore.
You can check more here
Below, I'm providing a full example of how to use it on a console app. Hope this might be helpful for someone in the future.
class Program
{
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<MyClass, MyClassDTO>();
});
IMapper mapper = config.CreateMapper();
var myClass = new MyClass(){
Id = 10,
Name = "Test"
};
var dst = mapper.Map<MyClass, MyClassDTO>(myClass);
Console.WriteLine(dst.Id);
}
}
class MyClass
{
public int Id {get;set;}
public string Name {get;set;}
}
public class MyClassDTO
{
public int Id {get;set;}
public string Name {get;set;}
}
Do not forget to include using AutoMapper;
Yes, but it appears to have a dependency on System.Web, which must be included too.
(See Mysterious disappearing reference for more details)