log4net wrapper doesn't log (during MSTest UTs) - c#

I have a wrapper class for log4net to log across multiple classes and assemblies within a group of dll-libraries. (This may be poor design, but.. don't ask)
The code basically looks like this:
public static class Logger
{
private static ILog log;
static Logger()
{
log = LogManager.GetLogger("abcd");
XmlConfigurator.Configure(new FileInfo("LoggerConfig.log4net"));
log.Info("=== NEW SESSION ===");
}
public static void Log(string message)
{
log.Debug(message);
}
}
Now.. if the static constructor was the static main routine of a simple executable, this works perfectly fine (I quickly made a console app to test it). But here it does not.
Since I deal with standalone assemblies, not executables, the only way I can easily test the logger is through MSTest. This may be a cause for this problem. I am not 100% sure if it maybe simply can't find the config file. It is copied at compilation and lies in the same folder as the assembly containing the logger class, so i think that should work.
bleh.. stuck since three hours. :T any suggestions?

My psychic debugger says that your LoggerConfig.log4net is not in the right directory. It is not deployed to your test directory and not found when running the test. That is why there is no log output. Deploy the file in the test directory and you will have you logging output.
EDIT: More precisely, you need to add it as a deployment file under Test->Edit Test Settings->>Deployment (as described here: How can I get "Copy to Output Directory" to work with Unit Tests? )

Have you tried to reference the config file by an absolute path? Maybe the root directory for MSTests is different to the Console App root dir?

Log4net uses configs in App.config? Try to put them also in the testlibrary?
I know the problem by Application with many libraries and there de config must only be in the Main-Application.

MsTest shadow copies things all over the place and as peer and Hinek say the location of the file is probably the issue. I find including files within the test assembly is one way to get around this.
Check out a previous answer here for details.

Related

What is causing ConfigurationManager not to be working for class library project when used in unit test?

What I'm using
I'm using:
.NET 6 with <Nullable>disable</Nullable>,
NUnit (3.13.3),
and System.Configuration.ConfigurationManager.
What I have
Currently, I have a simple solution consisting of 3 projects:
A class library,
a console app,
and a unit test project
The class library has a single class Foo which uses ConfigurationManager to read setting from configuration file
using System.Configuration;
namespace ClassLibrary;
public class Foo
{
public static string Bar = ConfigurationManager.AppSettings["setting"];
}
App.Config is located in the solution root, it's linked to each project and looks like this
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="setting" value="Kitty meow" />
</appSettings>
</configuration>
When I use the Foo class in console app the setting value is read from the file just fine
using ClassLibrary;
Console.WriteLine(Foo.Bar); // Bar property contains the correct value
Console.ReadKey();
However, when I try to use the Foo class in unit test, the value of Bar property is null
using ClassLibrary;
using NUnit.Framework;
namespace TestProject;
public class ConfigurationManagerTest
{
[Test]
public void MakeSureThatConfigurationManagerReadsFromFileCorrectly()
{
Assert.That(Foo.Bar, Is.Not.Null); // Bar property is null
}
}
The full source is available on GitHub:
https://github.com/dva-meow/UnitTestConfiguration
What I have tried
I've been trying to figure out what was causing this but had no success. Here's a list of what I've tried:
1. Copying the configuration file — In this SO post as well as in this one a few people mention that it's necessary to link/copy the App.Config file to the unit test project. This is working for the console app but not for the unit test (I've tried both linking and copying the file).
2. Making sure the configuration file is present in the executing directory — This SO answer says that:
When you compile an application, its app.config is copied to the bin directory with a name that matches your exe. For example, if your exe was named "test.exe", there should be a "text.exe.config" in your bin directory.
I made sure this was indeed the case for my project and I can confirm, that the renamed App.Config file is being copied to the bin directory of each project.
3. Investigating when initialization of static field occur — This SO answer suggests that:
Static classes are initialized prior to first use of any static member of that class. Unit Test code is no exception to this rule.
I was experimenting in debug for a while but haven't discovered anything useful. The correct value is present in the console app while in unit test it's always null, regardless of how many times I try to access the Bar property. (Also, I find this a bit odd as I don't believe it matters at all whether I assert the property for null right away or assign it to a variable before doing it.)
4. Experimenting with case-sensitivity — I tried renaming the configuration file to rule out the possibility that the ConfigurationManager is case-sensitive when used in unit tests.
What I need
I could probably come up with some sort of a workaround to avoid this issue but I would very much like to get to the bottom of this. All circumstances seem to be the same for both the console app as well as the unit test project and I'm really confused why it's not working the same in both cases.
What am I doing wrong?
Simple. You have an App.config but you aren't running your app. You are running NUnit. Each NUnit test assembly may have a config file of its own and you have to follow NUnit conventions rather than .NET conventions to make sure the config is used.
In this case, rename your config based on your test assembly name: e.g. my.test.assembly.dll.config and make sure it's in the same directory as the assembly.
See this (ancient but still basically correct) blog post for a more detailed explanation: http://charliepoole.org/technical/how-nunit-finds-config-files.html

Base class not finding app settings

This is somewhat of a general question but I haven't found much by googling it. I have a test framework that sets up an environment for testing purposes. My application consumes this framework through a reference path and runs manual tests just fine. However, once I ask the build server to run my test the framework complains it cannot find any of my app settings. The app.config file sits in my testing project for my application and I am sure it exists in the correct bin folder on my build server. I'm doing this in a C# .NET environment.
EDIT:
I'm not sure what to be more specific about. I would imagine it's something with the build server since it seems to work running tests locally but I have no clue what to look at. Nothing else about the build server is failing, just getting the app settings.
The framework is .NET 4.0 while the main project is 4.5. I'm using nunit to run the tests and running them outside the build process but using the Nunit gui fails at the same point.
The code that grabs the app settings is pretty basic:
string databaseName = ConfigurationManager.AppSettings["databaseName"];
EDIT
Snippet of my test:
public class UserServiceTests : DeployDBEveryFixtureBase
{
public UserServiceTests()
{
DBSetup("Core");
DBSetup("Postal");
DBSetup("Common");
}
private UserService userService = new UserService(string.Format("Data Source={0};Initial Catalog={1};User ID={2};Password={3};",
ConfigurationManager.AppSettings["targetServer"],
ConfigurationManager.AppSettings["databaseName"],
ConfigurationManager.AppSettings["userID"],
ConfigurationManager.AppSettings["password"]));
[Test]
public void UserService_Get()
{
// Act
User user = userService.GetUser(Guid.Empty, "*****", string.Empty);
// Assert
Assert.IsTrue(user.FirstName == "System");
}
}
The environment deployment is in the base class of DeployDBEveryFixtureBase. The DBSetup calls ensure that each database is deployed in the proper order. All of those seem to run fine and my tests complete but I still get the described error. If I look at the database I can all the datbases being properly deployed and then removed (the base class includes a TestFixtureTearDown) but it seems like the build process is trying to run it again.
Note: I am only building the solution file during this process. I do not currently have a .proj file in the build.
Without knowing much about your build server or how exactly you run your tests there is only one issue which comes to mind, that is Shadow Copying. Can you check if your unit tested DLLs are not shadow copied to some location where the app.config file doesn't exist?
Apparently my testing framework had an empty constructor that wasn't so empty. It looks like the app.config was being properly queried but was returning an empty result set since the empty constructor did not have the proper arguments. After removing the contents of the empty constructor my test ran fine. Thank you all for the help.

Visual Studio Setup, Custom Action to copy the setup file

I am developing a visual studio setup project. So far all has been good except uninstalling. The program did not create its own uninstaller, but instead the uninstall feature is inside of the setup.msi. So what I need to do is during the install, I need to copy the running setup.msi to the [TARGETDIR]. The easiest way I can think of is to use custom actions. I'm pretty lost on custom actions though, I don't understand where they go and every time I try to code one, its full of errors. Looking at other questions and answers, I have come up with the following:
[RunInstaller(true)]
public partial class CustomInstaller : System.Configuration.Install.Installer
{
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string path = this.Context.Parameters["targetdir"];
// Do something with path.
}
}
But I don't understand it. I see how it gets targetdir as that gets passed via the customactiondata. But, RunInstaller isn't known, nor is System.COnfiguration.Install.Installer. Where am I supposed to put this .cs file to make it work? Very confused.
This class should be placed in your application project. I believe it can be included in any project whose DLL is packaged by the setup project, but it usually makes the most sense to put it in the application project.
Make sure your application project references System.Configuration.Install.dll. That will resolve the reference to Installer.
RunInstaller is in the System.ComponentModel namespace (System.dll), so make sure you have a using System.ComponentModel statement at the top of the class file.
This question may help you understand custom actions more:
Why won't my Setup Project Perform my Custom Registration Process
This might help you get started, although it doesn't cover installer classes.
https://www.simple-talk.com/dotnet/visual-studio/getting-started-with-setup-projects/
Error 2835 is a bogus error related to trying to show a dialog, probably the error dialog, so it's masking whatever went wrong, most likely your code.
Make a log using msiexec /I /l*v and look for your error before the 2835.
You pass that targetdir value to your custom action like an argument list, so you'd say /targetdir="[TARGETDIR]\" and that's case-sensitive and including the brackets. It's picky because it gets merged with other arguments.
Beware that your code is not running in an application context. It's a call from the msiexec process running with the system account and a working directory of sysetm32. That means full paths need to be specified and any use of a profile-specific item (like Desktop, user profile location) will fail.

app.configs and MSTest Project - null reference for a connection string

When I try to run Unit Tests (mstest) I run into this issue.
The line of code:
_mainCnStr = System.Configuration.ConfigurationManager.
ConnectionStrings["main"].ConnectionString;
Comes back as a null reference
It doesn't do this in the main UI project when I run it. What is the right method to get this connection string setting seen by the Unit Test project? I tried embedded as a resource. I tried Copy Always. What is the right combination of settings that will fix this for me?
One thing to watch with MSTest (from the IDE at least); it doesn't run the tests in the regular output (bin) folder, and it doesn't respect the project's file inclusions ("Copy to Output Directory"). You often need to explicitly tell it (MSTest) which files to put into the test area. You will need to include the "app.config" in this list; either via the testrunconfig ("Deployment"), or by adding an attribute ([DeploymentItem]) to the affected test fixtures.
You should add an app.config to the unit test project. It won't automatically use the settings in UI application's app.config.
I'm assuming mstests are, like nunit tests, embedded in a seperate assembly which gets loaded by the testing application? In that case, you may need to create some test set-up code which loads in the configuration file.

Can a unit test project load the target application's app.config file?

I am unit testing a .NET application (.exe) that uses an app.config file to load configuration properties. The unit test application itself does not have an app.config file.
When I try to unit test a method that utilizes any of the configuration properties, they return null. I'm assuming this is because the unit test application is not going to load in the target application's app.config.
Is there a way to override this or do I have to write a script to copy the contents of the target app.config to a local app.config?
This post kind-of asks this question but the author is really looking at it from a different angle than I am.
EDIT: I should mention that I'm using VS08 Team System for my unit tests.
In Visual Studio 2008 I added the app.config file to the test project as an existing item and selected copy as link in order to make sure it's not duplicated. That way I only have one copy in my solution. With several test projects it comes in really handy!
The simplest way to do this is to add the .config file in the deployment section on your unit test.
To do so, open the .testrunconfig file from your Solution Items. In the Deployment section, add the output .config files from your project's build directory (presumably bin\Debug).
Anything listed in the deployment section will be copied into the test project's working folder before the tests are run, so your config-dependent code will run fine.
Edit: I forgot to add, this will not work in all situations, so you may need to include a startup script that renames the output .config to match the unit test's name.
Whether you're using Team System Test or NUnit, the best practice is to create a separate Class Library for your tests. Simply adding an App.config to your Test project will automatically get copied to your bin folder when you compile.
If your code is reliant on specific configuration tests, the very first test I would write validates that the configuration file is available (so that I know I'm not insane)
:
<configuration>
<appSettings>
<add key="TestValue" value="true" />
</appSettings>
</configuration>
And the test:
[TestFixture]
public class GeneralFixture
{
[Test]
public void VerifyAppDomainHasConfigurationSettings()
{
string value = ConfigurationManager.AppSettings["TestValue"];
Assert.IsFalse(String.IsNullOrEmpty(value), "No App.Config found.");
}
}
Ideally, you should be writing code such that your configuration objects are passed into your classes. This not only separates you from the configuration file issue, but it also allows you to write tests for different configuration scenarios.
public class MyObject
{
public void Configure(MyConfigurationObject config)
{
_enabled = config.Enabled;
}
public string Foo()
{
if (_enabled)
{
return "foo!";
}
return String.Empty;
}
private bool _enabled;
}
[TestFixture]
public class MyObjectTestFixture
{
[Test]
public void CanInitializeWithProperConfig()
{
MyConfigurationObject config = new MyConfigurationObject();
config.Enabled = true;
MyObject myObj = new MyObject();
myObj.Configure(config);
Assert.AreEqual("foo!", myObj.Foo());
}
}
If you have a solution which contains for example Web Application and Test Project, you probably want that Test Project uses Web Application's web.config.
One way to solve it is to copy web.config to test project and rename it as app.config.
Another and better solution is to modify build chain and make it to make automatic copy of web.config to test projects output directory. To do that, right click Test Application and select properties. Now you should see project properties. Click "Build Events" and then click "Edit Post-build..." button. Write following line to there:
copy "$(SolutionDir)\WebApplication1\web.config" "$(ProjectDir)$(OutDir)$(TargetFileName).config"
And click OK. (Note you most probably need to change WebApplication1 as you project name which you want to test). If you have wrong path to web.config then copy fails and you will notice it during unsuccessful build.
Edit:
To Copy from the current Project to the Test Project:
copy "$(ProjectDir)bin\WebProject.dll.config" "$(SolutionDir)WebProject.Tests\bin\Debug\App.Config"
This is a bit old but I found a better solution for this. I was trying the chosen answer here but looks like .testrunconfig is already obsolete.
1. For Unit Tests, Wrap the config is an Interface (IConfig)
for Unit tests, config really shouldn't be part of what your testing so create a mock which you can inject. In this example I was using Moq.
Mock<IConfig> _configMock;
_configMock.Setup(config => config.ConfigKey).Returns("ConfigValue");
var SUT = new SUT(_configMock.Object);
2. For Integration test, dynamically add the config you need
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if(config.AppSettings.Settings[configName] != null)
{
config.AppSettings.Settings.Remove(configName);
}
config.AppSettings.Settings.Add(configName, configValue);
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection("appSettings");
This is very easy.
Right click on your test project
Add-->Existing item
You can see a small arrow just beside the Add button
Select the config file click on "Add As Link"
If you are using NUnit, take a look at this post. Basically you'll need to have your app.config in the same directory as your .nunit file.
If you application is using setting such as Asp.net ConnectionString you need to add the attribute HostType to your method, else they wont load even if you have an App.Config file.
[TestMethod]
[HostType("ASP.NET")] // will load the ConnectionString from the App.Config file
public void Test() {
}
I use NUnit and in my project directory I have a copy of my App.Config that I change some configuration (example I redirect to a test database...). You need to have it in the same directory of the tested project and you will be fine.
I couldn't get any of these suggestions to work with nUnit 2.5.10 so I ended up using nUnit's Project -> Edit functionality to specify the config file to target (as others have said it needs to be in the same folder as the .nunit file itself). The positive side of this is that I can give the config file a Test.config name which makes it much clearer what it is and why it is)
In my case, I performed the following steps to resolve my issue. I'm using Visual Studio Professional 2022, NUnit 3.13.3 and .NET 6.
I added an App.config file to the test project per the suggestions but ConfigurationManager.AppSettings still did not load any of my app settings.
In my unit test, I evaluated the value of ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None).FilePath and determined that it was looking for a file named testhost.dll.config
In the unit test project, I renamed App.config to testhost.dll.config.
Your unit tests are considered as an environment that runs your code to test it. Just like any normal environment, you have i.e. staging/production. You may need to add a .config file for your test project as well. A workaround is to create a class library and convert it to Test Project by adding necessary NuGet packages such as NUnit and NUnit Adapter. it works perfectly fine with both Visual Studio Test Runner and Resharper and you have your app.config file in your test project.
And finally debugged my test and value from App.config:

Categories

Resources