So I've done some looking around. Most of the threads I find seem related to people wanting to run log4net in their actual tests. Meaning they want log entries in their test class. I don't want log entries from my actual tests. But I do want the expected log entries from the code I am testing. This is my first time using Log4net. If I run the code on it's own, the log entries work. If I run a test, no log entries though. Im guessing it's not initialized properly or perhaps I don't have log4net setup correctly in my UnitTest (in appconfig or assembly maybe??). This is an MVC 5 application. Here is a basic example.
Nunit Test (basics):
namespace MyUnitTests
[TestFixture]
public class MyTestClass
{
[Test]
public void MyTest
{
//arrange
var testVar = #"string";
//act
MyProjectClass.Method(testVar);
//assert something
}
}
so over in MyProject I have (basics):
public class MyProjectClass : Controller
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(MyProjectClass));
public static void Method(string myString)
{
//does whatever
log("added value");
}
So I've obviously simplified this for the discussion. But when I run my actual test, my test passes, the values and the outcome are all as they should be. I just never see the log entry in the log for method I just tested. Im sure I'm missing something simple. Any help would be greatly appreciated. Thanks!
If you look in the app.config for your main project, you should see a log4net config section that specifies the location of the log file.
You first need to add log4net to your <configSections> like this:
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler, log4net, Version=1.2.10.0,
Culture=neutral, PublicKeyToken=1b44e1d426115821" />
</configSections>
You can then add a <log4net> section to the <configuration> tag. Documentation for this can be found here.
I just want to add the above answer. In addition to setting the appconfig in your UnitTest the same as your Application webconfig (editing the value for log file location if you want) you also need to add the assembly entry as you would have for your application already (changing web.config to app.config in my case).
//logger
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "app.config", Watch = true)]
You'll also need to initialize the logger (at least I did) in your unit test. So basically I added this in my arrange of my test if wanted my logging in the method I was testing to fire. Add something like this in your arrange or setup for you test class possibly:
[Test]
public void MyTest
{
//arrange
log4net.ILog log = log4net.LogManager.GetLogger(typeof(MyTestClass));
log4net.Config.XmlConfigurator.Configure();
}
Related
I have a straightforward unit test class like so:
[TestClass]
public class SomeTests
{
[TestMethod]
public void Test1()
{
// make some assertions
}
[TestMethod]
public void Test2()
{
// make some assertions
}
// ...
[TestMethod]
public void Test50()
{
// make some assertions
}
}
and I basically want to run Test1..Test50 twice via the "Run All" command. Once with App.config like so:
<appSettings>
<add key="SomeSetting" value="true"/>
</appSettings>
and once with App.config like so:
<appSettings>
<add key="SomeSetting" value="false"/>
</appSettings>
Being lazy, I don't want to refactor & parameterize 50 tests. And obviously I don't want 50 duplicate tests.
I'm having a bit of a brain fart over this, apologies if it's blindingly obvious.
make SomeTests abstract.
Add two new projects, with the two different .config files.
In each project add a TestClass that inherits from SomeTests.
Run all will now run both sets of tests.
Create two Test Settings. Click Enable Deployment under the Deployment section. Add your two different App.Config files (one to each Test Settings). Execute your tests, then swap to the other TestSettings and execute them again.
Might be a way to run with all testsettings, but I haven't found it.
I have a C# unit test project with application settings in the app.config file. I am testing a class that exists in a different project. That class depends on both, ConfigurationManager.AppSettings and ConfigurationManager.ConnectionStrings.
The project that the class being tested resides in does not have an app.config file. I would have thought that because the class is being instantiated in the context of the unit test project that it would use the unit test project's app.config file. Indeed, that does seem to be the case for the connection string.
The class retrieves the connection string without any issues. However, when the class tries to retrieve any application settings the configuration manager always returns null. What is going on here?
Edit 1
I thought maybe it would be a good idea to try load some settings in the test project to see what happens. I tried to load the setting in the unit test immediately before calling the code that instantiates the class in the external project. Same result, nothing. I guess I can exclude the other project from the equation for the time being.
Here is an excerpt from my config file:
<configSections>
<sectionGroup name="applicationSettings"
type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MyNamespace.Properties.Settings"
type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</sectionGroup>
</configSections>
...
<applicationSettings>
<MyNamespace.Properties.Settings>
<setting name="Bing_Key"
serializeAs="String">
<value>...</value>
</setting>
</MyNamespace.Properties.Settings>
</applicationSettings>
and here is how I am attempting to load the setting:
string test = System.Configuration.ConfigurationManager.AppSettings["Bing_Key"];
Consider refactoring your code that accesses the config to use a wrapper. Then you can write mocks for the wrapper class and not have to deal with the importing of the configuration file for the test.
In a library that is common to both, have something like this:
public interface IConfigurationWrapper {
string GetValue(string key);
bool HasKey(string key);
}
Then, in your libraries that need to access config, inject an instance of this interface type into the class that needs to read config.
public class MyClassOne {
private IConfigurationWrapper _configWrapper;
public MyClassOne(IConfigurationWrapper wrapper) {
_configWrapper = wrapper;
} // end constructor
public void MethodThatDependsOnConfiguration() {
string configValue = "";
if(_configWrapper.HasKey("MySetting")) {
configValue = _configWrapper.GetValue("MySetting");
}
} // end method
} // end class MyClassOne
Then, in one of your libraries, create an implementation that depends on the config file.
public class AppConfigWrapper : IConfigurationWrapper {
public string GetValue(string key) {
return ConfigurationManager.AppSettings[key];
}
public bool HasKey(string key) {
return ConfigurationManager.AppSettings.AllKeys.Select((string x) => x.ToUpperInvariant()).Contains(key.ToUpperInvariant());
}
}
Then, in the code that calls your class.
//Some method container
MyClassOne dataClass = new MyClassOne(new AppConfigWrapper());
dataClass.MethodThatDependsOnConfiguration();
Then in your test, you are free from dependency bondage. :) You can either create a fake version that implements IConfigurationWrapper and pass it in for your test, where you hard-code the return values from the GetValue and HasKey functions, or if you're using a mocking library like Moq:
Mock<IConfigurationWrapper> fakeWrapper = new Mock<IConfigurationWrapper>();
fakeWrapper.Setup((x) => x.GetValue(It.IsAny<string>)).Returns("We just bypassed config.");
MyClassOne testObject = new MyClassOne(fakeWrapper.Object);
testObject.MethodThatDependsOnConfiguration();
Here is an article that covers the concept (albeit, for web forms, but the concepts are the same): http://www.schwammysays.net/how-to-unit-test-code-that-uses-appsettings-from-web-config/
You mentioned settings in the project properties. See if you can access the setting this way:
string test = Properties.Settings.Default.Bing_Key;
You may need to get the executing assembly of where the project settings file is defined, but try this first.
EDIT
When using Visual Studio's project settings file, it adds stuff to your app.config and creates the app.config if it is not present. ConfigurationManager CAN'T touch these settings! You can only get to these specific generated project.settings file from using the above static method. If you want to use ConfigurationManager, you will need to hand write your app.config. Add your settings to it like so:
<appSettings>
<add key="bing_api" value="whatever"/>
</appSettings>
If you're using .NET Core your problem could be a known issue caused by the fact that the test process runs as testhost.dll (or testhost.x86.dll), which means the runtime config file is expected to be named "testhost.dll.config" (or "testhost.x86.dll.config"), instead of the app.config output (ex: "MyLibrary.Tests.dll.config").
To fix it, add the code below to your project file (.csproj, etc) inside of root node <Project>. During build, two copies of app.config will be put in the output directory and named "testhost.dll.config" and "testhost.x86.dll.config", which will get your app settings working again. (You only need 1 of these files but it's safer to include both.)
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.dll.config" />
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.x86.dll.config" />
</Target>
I recommend app.config only as a temporary solution. If you're like me you might have run into the problem while upgrading a .NET Framework project to .NET Core and needed a quick fix. But don't forget to look into the new, more elegant solutions provided by .NET Core for storing app settings.
And then he screamed "NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO".
Cite: I have a C# unit test project with application settings in the app.config file. I am testing a class that exists in a different project. That class depends on both, ConfigurationManager.AppSettings and ConfigurationManager.ConnectionStrings.
You don't do this. EVER!!!! Why? because you have now created a dependency. Instead, use dependency injection so the class can do its work without having to peak into the configuration file that belongs to the application.
i am first time using any DI framework called unity app block. i am getting error. the error is :-
The type name or alias ILogger could not be resolved. Please check your configuration file and verify this type name.
i was trying inject dependency from out side into my main proj. suppose i want to save data to anywhere by dependency. say suppose i want to save data to file or console, database etc.
here i am telling you how i develop my app for incorporating Unity.
first i create a class library project called "Ilogger" it has only one interface. full code of this interface
namespace Ilogger
{
public interface ILog
{
void Write(string msg);
}
}
secondly i create a class library project called "ConsoleWriter" it has only one class which inherit Ilogger interface.so i just add the reference of Ilogger project into ConsoleWriter proj. full code of this ConsoleWriter
namespace ConsoleWriter
{
public class ConsoleWriter : Ilogger.ILog
{
#region ILog Members
public void Write(string msg)
{
Console.WriteLine(msg);
Console.ReadLine();
}
#endregion
}
}
3rd step i create a class library project called "FileWriter
" it has only one class which inherit Ilogger interface.so i just add the reference of Ilogger project into FileWriter proj. full code of this FileWriter
namespace FileWriter
{
public class FileWriter : Ilogger.ILog
{
#region IWriter Members
public void Write(string msg)
{
using (StreamWriter streamWriter = new StreamWriter("c:\\TestUnity.txt", true))
{
streamWriter.WriteLine(msg);
}
}
#endregion
}
}
next i create my win apps from where i inject dependecy at runtime. in this project i add some dll reference of unity block and those are.
Microsoft.Practices.Unity
Microsoft.Practices.Unity.Configuration
system.configuration
i add one app.config file and it has entry like
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias type="ILogger.ILog, ILogger" alias="ILogger" />
<namespace name="ILogger.ILog"/>
<container>
<register mapTo="FileWriter.FileWriter, FileWriter" name="MyFileWriter" type="ILogger"/>
<register mapTo="ConsoleWriter.ConsoleWriter, ConsoleWriter" name="MyConsoleWriter" type="ILogger"/>
</container>
</unity>
</configuration>
here is the main code from where error is thrown
string strCountryCode = "USA";
IDictionary<string, string> loggers = new Dictionary<string, string>();
loggers.Add("USA", "MyFileWriter");
loggers.Add("GBR", "MyConsoleWriter");
IUnityContainer container = new UnityContainer();
UnityConfigurationSection section = (UnityConfigurationSection)System.Configuration.ConfigurationManager.GetSection("unity");
//container.LoadConfiguration();
section.Containers.Default.Configure(container);
Ilogger.ILog logger = container.Resolve<Ilogger.ILog>(loggers[strCountryCode]);
logger.Write("Hello World");
this line giving error section.Containers.Default.Configure(container);
i am using DI framework unity first time so i am not being able to catch what mistake i made. so please anyone help me to get the error and tell me how to fix it.
thanks
there may be several issues;
in the entry point of application, where you are trying to read unity configuration, you need to add the assembly which contains ILog interface; the assembly may be missing.
you need to add aliases using full paths; the full path needs to include the assembly name.
in order to observe exact problem, instead of using a configuration file, try to make registrations in the code.
I need to use some WPF components in an NUnit unit test. I run the test through ReSharper, and it fails with the following error when using the WPF object:
System.InvalidOperationException:
The calling thread must be STA, because many UI components require this.
I've read about this problem, and it sounds like the thread needs to be STA, but I haven't figured out how to do this yet. What triggers the problem is the following code:
[Test]
public void MyTest()
{
var textBox = new TextBox();
textBox.Text = "Some text"; // <-- This causes the exception.
}
You should add the RequiresSTA attribut to your test class.
[TestFixture, RequiresSTA]
public class MyTestClass
{
}
With more recent versions, the attribute has changed :
[Apartment(ApartmentState.STA)]
public class MyTestClass
{}
Have you tried this?
... simply create an app.config file for the dll you are attempting to test, and add some NUnit appropriate settings to force NUnit to create the test environemnt as STA instead of MTA.
For convenience sake, here is the config file you would need (or add these sections to your existing config file):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="NUnit">
<section name="TestRunner" type="System.Configuration.NameValueSectionHandler"/>
</sectionGroup>
</configSections>
<NUnit>
<TestRunner>
<add key="ApartmentState" value="STA" />
</TestRunner>
</NUnit>
</configuration>
Background
I am writing a class library assembly in C# .NET 3.5 which is used for integration with other applications including third-party Commercial-Off-The-Shelf (COTS) tools. Therefore, sometimes this class library will be called by applications (EXEs) that I control while other times it will be called by other DLLs or applications that I do not control.
Assumptions
I am using C# 3.0, .NET 3.5 SP1, and Visual Studio 2008 SP1
I am using log4net 1.2.10.0 or greater
Constraints
Any solution must:
Allow for the class library to enable and configure logging via it's own configuration file, if the calling application does not configure log4net.
Allow for the class library to enable and configuring logging via the calling applications configuration, if it specifies log4net information
OR
Allow for the class library to enable and configuring logging using it's own configuration file at all times.
Problem
When my stand-alone class library is called by a DLL or application that I do not control (such as a third-party COTS tool) and which doesn't specify log4net configuration information, my class library is unable to do any of it's logging.
Question
How do you configure and enable log4net for a stand-alone class library assembly so that it will log regardless if the calling application provides log4net configuration?
Solution 1
A solution for the first set of constraints is to basically wrap the log4net.LogManager into your own custom LogManager class like Jacob, Jeroen, and McWafflestix have suggested (see code below).
Unfortunately, the log4net.LogManager class is static and C# doesn't support static inheritance, so you couldn't simply inherit from it and override the GetLogger method.
There aren't too many methods in the log4net.LogManager class however, so this is certainly a possibility.
The other drawback to this solution is that if you have an existing codebase (which I do in my case) you would have to replace all existing calls to log4net.LogManager with your wrapper class. Not a big deal with today's refactoring tools however.
For my project, these drawbacks outweighed the benefits of using a logging configuration supplied by the calling application so, I went with Solution 2.
Code
First, you need a LogManager wrapper class:
using System;
using System.IO;
using log4net;
using log4net.Config;
namespace MyApplication.Logging
{
//// TODO: Implement the additional GetLogger method signatures and log4net.LogManager methods that are not seen below.
public static class LogManagerWrapper
{
private static readonly string LOG_CONFIG_FILE= #"path\to\log4net.config";
public static ILog GetLogger(Type type)
{
// If no loggers have been created, load our own.
if(LogManager.GetCurrentLoggers().Length == 0)
{
LoadConfig();
}
return LogManager.GetLogger(type);
}
private void LoadConfig()
{
//// TODO: Do exception handling for File access issues and supply sane defaults if it's unavailable.
XmlConfigurator.ConfigureAndWatch(new FileInfo(LOG_CONFIG_FILE));
}
}
Then in your classes, instead of:
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
Use:
private static readonly ILog log = LogManagerWrapper.GetLogger(typeof(MyApp));
Solution 2
For my purposes, I have decided to settle on a solution that meets the second set of constraints. See the code below for my solution.
From the Apache log4net document:
"An assembly may choose to utilize a named logging repository rather than the default repository. This completely separates the logging for the assembly from the rest of the application. This can be very useful to component developers that wish to use log4net for their components but do not want to require that all the applications that use their component are aware of log4net. It also means that their debugging configuration is separated from the applications configuration. The assembly should specify the RepositoryAttribute to set its logging repository."
Code
I placed the following lines in the AssemblyInfo.cs file of my class library:
// Log4Net configuration file location
[assembly: log4net.Config.Repository("CompanyName.IntegrationLibName")]
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "CompanyName.IntegrationLibName.config", Watch = true)]
References
LogManagerMembers
Jacob's Answer
Jeroen's Answer
McWafflestix's Answer
log4net Manual - Repositories
log4NET from a class library (dll)
You can probably code something around the XmlConfigurator class:
public static class MyLogManager
{
// for illustration, you should configure this somewhere else...
private static string configFile = #"path\to\log4net.config";
public static ILog GetLogger(Type type)
{
if(log4net.LogManager.GetCurrentLoggers().Length == 0)
{
// load logger config with XmlConfigurator
log4net.Config.XmlConfigurator.Configure(configFile);
}
return LogManager.GetLogger(type);
}
}
Then in your classes, instead of:
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));
Use:
private static readonly ILog log = MyLogManager.GetLogger(typeof(MyApp));
Of course, it would be preferable to make this class a service and dynamically configure it with the IoC container of your choice, but you get the idea?
EDIT: Fixed Count() problem pointed out in comments.
In your code you can check if there are any loggers via
log4net.LogManager.GetCurrentLoggers().Count()
You could then for example use an XmlConfigurator to load a default configuration from a file:
log4net.Config.XmlConfigurator.Configure(configFile)
You could do the initialization in a static or regular constructor.
class Sample
{
private static readonly log4net.ILog LOG;
static Sample()
{
if (log4net.LogManager.GetCurrentLoggers().Count() == 0)
{
loadConfig();
}
LOG = log4net.LogManager.GetLogger(typeof(Sample));
}
private static void loadConfig()
{
/* Load your config file here */
}
public void YourMethod()
{
LOG.Info("Your messages");
}
}
In your standalone class library, have a singleton which loads the log4net configuration file using the log4net.Config.XmlConfigurator.
Specifically, you can define all of your code to use your own custom logging class; this class can just be a simple wrapper of the log4net logging calls, with one addition; make a static member which contains the log information you want to log to; initialize that with a call to the XmlConfigurator in the static constructor for that class. That's all you have to do.
You can find a good description here:
log4net: A quick start guide
As the article describes, to configure it fore each assembly separately, create an XML file for your assembly named AssemblyName.dll.log4net and place the following XML code into it:
<?xml version="1.0" encoding="utf-8"?>
<log4net debug="false">
<appender name="XmlSchemaFileAppender" type="log4net.Appender.FileAppender">
<file value="AppLog.xml" />
<appendToFile value="true" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.XmlLayout" />
</appender>
<root>
<level value="WARN" />
<appender-ref ref="XmlSchemaFileAppender" />
</root>
</log4net>
It further describes, to instantiate a new logger, simply declare it as a variable for the entire class as follows:
public class LogSample
{
private static readonly log4net.ILog Log
= log4net.LogManager.GetLogger(typeof(LogSample));
// Class Methods Go Here...
}
You can then use the private variable Log inside your class like:
Log.Info("Sample message");
Likewise you can use Log.Error("Error occurred while running myMethod", ex) to log errors along with the exception details.
What I found is the following:
Don't forget to call log4net.Config.XmlConfigurator.Configure(); to activate your configuration
If you need to know the path of the file(s) written, here some code how to obtain it from Log4Net
I hope this helps.
This works for me for a shared library
protected static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.Assembly.GetExecutingAssembly().GetType());