Mock Log Singleton in Unit Test - c#

I have a logger class, which purpose is to be called from whatever class in my solution, who decides to log something.
I added an interface, which is why I applied a singleton pattern, and didn't use a static class.
My LogManager implementation (singleton):
https://pastebin.com/NHKmbj9c
I wanted to write simple unit tests, which are supposed to use local variables, testing the functionality of each ILogger methods, but as soon as my first Unit has passed, the Singleton will stay initialized in context, making subsequent unit tests to fail (while they are trying to Initialize the singleton...).
Unit Test:
[TestClass]
public class LogManagerTests
{
[TestMethod]
public void Error_ExpectedErrorLevel_ShouldBe_Error()
{
// Arrange
var actualLevel = ErrorLevel.Warning;
const ErrorLevel expectedLevel = ErrorLevel.Error;
var iLogger = LogManager.GetInstance;
iLogger.Initialize((level, msg) => { actualLevel = level; }, null);
// Act
iLogger.Error(new Exception(), string.Empty);
// Assert
Assert.AreEqual(expectedLevel, actualLevel);
}
[TestMethod]
public void Debug_ExpectedErrorLevel_ShouldBe_Verbose()
{
// Arrange
var actualLevel = ErrorLevel.Warning;
const ErrorLevel expectedLevel = ErrorLevel.Verbose;
var iLogger = LogManager.GetInstance;
iLogger.Initialize(null, (level, msg, ex) => { actualLevel = level; });
// Act
iLogger.Debug(string.Empty);
// Assert
Assert.AreEqual(expectedLevel, actualLevel);
}
}
Another tought is to initialize the LogManager as a private global variable within my TestClass, but this could give race conditions if the Unit test runs async, as multiple methods then will access the same output variable, which may override each others.
Is it possible to UnitTest a singleton in any way?
The design does not allow me to refactor the LogManager, and remove the singleton pattern from it.

It's possible to unit test a singleton, you just need to think about it differently. Don't try so hard to change your methodology to fit the test. Think about creating a method that is used only for testing, LogManager.Uninitialize().
Call this after every test in this group of tests to ensure your singleton is set back to a testable state.
[TestCleanup()]
public void Cleanup()
{
LogManager.Uninitialize();
}
It may not be pure but I think it's fine to write in a diagnostics method every once in a while. It's better than having bad test coverage where you need good test coverage.

Related

Unit Test - How to test a `void` method that just inserts a log message (Serilog)

I need to test a method of type void, it just inserts a message on my LOG variable using a LOG framework (Serilog).
See the implementation example:
public class MyClass
{
public MyClass(ILogger<IProcess> logger)
{
this.logger = logger;
}
private readonly ILogger logger;
//...Anothers methods...
public void LogInit(Guid processId, string folder, string[] args)
{
var myObject = new
{
ProcessId = processId,
Folder = folder,
Arguments = args
};
this.logger.LogWarning("{#myObject}", myObject);
}
}
In this scenario, we would need to create a test for the LogInit (...)
Does this method really need to be tested? Or rather, does it make sense for it to exist?
I was reading something about:
"If your method has no side effects, and doesn’t return anything, then it’s not doing anything."
In this case, the method was only created "separately" to maintain the organization of the code and separate the responsibilities of the class a bit more.
That makes sense?
EDIT: #PeterBons
We do not have access to the messages registered in the Interface (logger), like: this.logger.GiveMeWarningLogs()...
If you're using Serilog, you can use the Test Correlator Sink to see what log events a method call produced.
[TestMethod]
public void A_test()
{
var myClass = new MyClass(new LoggerConfiguration().WriteTo.TestCorrelator().CreateLogger());
using (TestCorrelator.CreateContext())
{
myClass.LogInit();
TestCorrelator.GetLogEventsFromCurrentContext()
.Should().ContainSingle()
.Which.MessageTemplate.Text
.Should().Be("{#myObject}");
}
}
You have to mock your logger and check whether LogWarning method was called. You can use Moq for this. Also if you want to test LogInit you need to make this public or internal with defining [InternalVisibleTo('someTestProjName')]
Test method will looks like this (xUnit):
public void ShouldCallLogWarning()
{
var loggerMock = new Mock<ILogger>();
loggerMock.Setup(_ => _.LogWarning(It.IsAny<string>(), It.IsAny<object>(), null);
var myClass = new MyClass(loggerMock.Object);
//
myClass.LogInit(Guid.NewGuid(), "folderPath", null)
//
_loggerMock.Verify(_ => _.LogWarning(It.IsAny<string>(), It.IsAny<string>(), null), Times.Once());
}
Your method is not returning anything so it would be harder to test if for sure.
Right now the only thing your method does is prepare the logged object for the Serilog library. If you had more complicated logic for the creation of that object you could extract it into it's own method and have it return that log object then it would be easy to test that instead. The benefit of the method you have created is that it creates an extra layer so that if you decide to change the logging library you would only do it in one place.

Testing if a messagebox was successfully shown in a unit test

hopefully it's pretty simple but I'm stuck trying to figure this out.
If I have a simple class with a method which is supposed to do something and then show a messagebox via MessageBox.Show("") when some values are in a certain state, how do I write a test/tests that can detect if a the messagebox was shown when expected and/or not shown when not expected?
You can't really test that though Unit tests. There is an easy way to test if the code was called correctly though.
I would hide showing the MessageBox behind an interface. Then mock that interface and create a counter or something like that in your mock implementation. Of course you can make this as specific as you need, providing the message shown, etc.
You shouldn't test that a MesssageBox appears, though it is possible to do so with one or another UI automation framework (like https://msdn.microsoft.com/en-us/library/ms747327(v=vs.110).aspx).
But it won't be a unittest. It would be an automated UI test.
So, if you want to create a proper unittest, then your class should be injectable with some
public interface IMessageBox
{
void Show(String message);
}
public class SUT
{
public SUT(IMessageBox messageBox)
{
this._messageBox = messageBox;
}
public void Test()
{
this._messageBox.Show("TEST);
}
}
So that you can mock that IMessageBox inside the unittest.
For example, with Moq:
[TestMethod]
public void Test()
{
// Arrange
var messageBox = new Mock<IMessageBox>();
messageBox
.Setup(m =>
m.Show("TEST))
.Verifiable();
var sut = new SUT(messageBox.Object);
// Act
sut.Test();
// Verify
messageBox.Verify();
}

Asynchronous initialization and its unit testing

Background
I need some class to perform background initialization, which should start in constructor. Currently I'm using a Task which is started by constructor, and then all operations, depending on that initialization wait for that Task completion.
Please take a look at the following simplified example:
interface IEntry {}
interface IRepository
{
IQueryable<IEntry> Query { get; }
void Add(IEntry entry);
}
class PrefetchedRepository : IRepository
{
private readonly Task _prefetchingTask;
private readonly ICollection<IEntry> _entries = new List<IEntry>();
private readonly IRepository _underlyingRepository;
public PrefetchedRepository(IRepository underlyingRepository)
{
_underlyingRepository = underlyingRepository;
// Background initialization starts here
_prefetchingTask = Task.Factory.StartNew(Prefetch);
}
public IQueryable<IEntry> Query
{
get
{
EnsurePrefetchCompleted();
return _entries.AsQueryable();
}
}
public void Add(IEntry entry)
{
EnsurePrefetchCompleted();
_entries.Add(entry);
_underlyingRepository.Add(entry);
}
private void EnsurePrefetchCompleted()
{
_prefetchingTask.Wait();
}
private void Prefetch()
{
foreach (var entry in _underlyingRepository.Query)
{
_entries.Add(entry);
}
}
}
This works. The problem starts when I want to test initialization in Unit Test. I'm creating the instance and providing the mock of underlying repository. I want to ensure that all entries were fetched from the mock as expected.
[TestFixture]
public class PrefetchingRepositoryTests
{
[Test]
public void WhenInitialized_PrefetchingIsDone()
{
// Arrange
var underlyingRepositoryMock = A.Fake<IRepository>();
// Act
var target = new PrefetchedRepository(_underlyingRepository);
// Assert
underlyingRepositoryMock.CallsTo(r => r.Query).MustHaveHappened(Repeated.Exactly(1));
}
}
As you can imagine, most of the time fails, because actually initialization didn't started yet at the assertion point.
Questions
Question 1 - Initialization: Is there more elegant way of asynchronous initialization rather than starting task in constructor and waiting for it in all dependent operations?
Question 2 - Testing: I thought of 2 possible ways to solve race between the test and testee:
Using event handle to the test:
[Test]
public void WhenInitialized_PrefetchingIsDone()
{
// Arrange ...
var invokedEvent = new ManualResetEvent(false);
underlyingRepositoryMock.CallsTo(r => r.Query).Invokes(_ => invokedEvent.Set());
// Act ...
// Assert
Assert.True(invokedEvent.WaitOne(1000));
}
Exposing EnsurePrefetchCompleted method as internal and using it in the Unit Test (assuming usage of [assembly: InternalsVisibleTo("...")])
The problem with both solutions is that in case of failure time duration long (actually in the second case - it is limited by test timeout).
Is there any simpler way to do this kind of testing?
Extract the prefetch logic into a separate Prefetcher class and when testing mock the Prefetcher with something that does the fetching without the use of a separate thread.
This will allow you to do the white-box testing of your PrefetchedRepository which I see you are attempting to do with
underlyingRepositoryMock.CallsTo(r => r.Query).MustHaveHappened(Repeated.Exactly(1)); (I would never do white-box testing, but that's just me.)
Once you are done with your white box testing, you can then do black-box testing of your PrefetchedRepository, without concern as to how it works internally. (Whether it invokes other objects to do its job, how many times it invokes them, etc.) Therefore, your testing code will not need to guess the point in time when it is okay to check whether query has been invoked, because it will not be concerned at all with whether query was invoked or not. Essentially, your testing code will be testing against interface IRepository, not against class PrefetchedRepository.
Don't expose a instance which is in invalid state. Client code may often notice a delay when calling any members in PrefetchedRepository just because the underlying repository is slow. You're trying to be clever by hiding these details by hiding all the grotty waiting logic inside EnsurePrefetchCompleted which client code doesn't even know. But this may surprise the client why does even this takes a lot of time??
Better approach is to expose the Task in the public surface of the API and let the client code await it before it does anything with the repository instance.
Something like this:
class PrefetchedRepository : IRepository
{
private readonly Task _prefetchingTask;
private readonly ICollection<IEntry> _entries = new List<IEntry>();
private readonly IRepository _underlyingRepository;
public PrefetchedRepository(IRepository underlyingRepository)
{
_underlyingRepository = underlyingRepository;
// Background initialization starts here
_prefetchingTask = Task.Factory.StartNew(Prefetch);
}
public Task Initialization
{
get
{
return _prefetchingTask;
}
}
...
}
Then you could do
var repo = new PrefetchedRepository(someOtherSlowRepo);
await repo.Initialization;
//Then do whatever with the repo.
Of course delete that EnsurePrefetchCompleted method and all calls to it.
But I do understand this introduces the smell so-called Temporal Coupling.
Better design is to introduce a Factory which does this for you.
public class PrefetchedRepositoryFactory
{
public Task<IRepository> CreateAsync()
{
//someOtherSlowRepo can be a parameter or instance field of this class
var repo = new PrefetchedRepository(someOtherSlowRepo);
await repo.Initialization;
return repo;
}
}
Then you could simply do
var repo = await prefetchedRepositoryFactory.CreateAsync();
//Do whatever with repo.
If you do so, you don't have to take any special care for testing as you'll always have the fully constructed repository in hand.
You can await inside the Test methods; Most of the major unittesting frameworks supports async Task returning methods.

Unit test factory method for object with dependencies

I'm new in unit testing.
Its suggested to use Factory method to create instance of class under test for maintainability reasons.
Like:
public class StringCalculatorTests
{
[Fact]
public void Add_EmptyString_ReturnZero()
{
var calculator = CreateCalculator();
int result = calculator.Add("");
result.Should().Be(0);
}
private static StringCalculator CreateCalculator()
{
//Some difficult object creation
var logger = Substitute.For<ILogger>();
var calculator = new StringCalculator(logger);
calculator.Initialize();
return calculator;
}
}
Everything nice: if API changes - i will change StringCalculator creation only in one place, not in every tests.
But what if i need to change a return value for some method of ILogger. Or I will use ILogger not as stub but as a mock:
[Fact]
public void Add_EmptyString_LogAboutEmptyInput()
{
var loggerMock = Substitute.For<ILogger>();
var calculator = new StringCalculator(loggerMock);
calculator.Initialize();
calculator.Add("");
logger.Received("Empty input.");
}
Now i can't use factory method, and if there are changes in API - i should go through my tests to change it.
I thought about property injection - but it may be not good local default for ILogger for example.
(i know - we usually have good default for logger, but it can be some another dependency)
I thought about optional parameters for factory method. But it seems to have logic. It's very simple but still logic.
Is there any good approach to solve this? Or it's good enough and it's a common situation to create instance just in class when we need it?
You can overload your factory method to accept a mock logger.
private static StringCalculator CreateCalculator(ILogger logger)
{
var calculator = new StringCalculator(logger);
calculator.Initialize();
return calculator;
}
Then you can create your mock logger in the test (possibly a separate factory method for the logger if the same mocking is used in multiple tests)
[Fact]
public void Add_EmptyString_LogAboutEmptyInput()
{
var loggerMock = //whatever code you need to set up your mock
var calculator = CreateCalculator(loggerMock);
calculator.Add("");
logger.Received("Empty input.");
}

Any way to intruct MsTest when to run a Test Method?

Is there a way or approach like a method decorator or attribute for a test method that can say for example:
"Run Method C Before running Method B"
So basically you are creating a dependancy between C and B. I know tests are better off being atomic and should be but sometimes in it's better to keep your tests small and to the point. It makes sense not run a 'RemoveItem' test method when the item it is looking for is simply not there.
Most people would add the item before hand and then test to see if they can remove - 'All In the same test'. I don't like this approach and want to make my tests smaller, more to to point and more atomic as possible.
Like you said, you dont want interdependencies between your test. If you are not comfortable having an "Add" before the "Remove" in your remove test, thus testing the Add method in the wrong place, then I recommend using testInitialize to setup some objects the tests can act on. I do however recommend the practice of actually running Add before you run Remove, in the test of Remove.
[Testclass]
public class TestStacks
{
private Stack<string> emptyStack;
private Stack<string> singleItemStack;
[TestInitialize]
public void Setup()
{
singleItemStack = new Stack<string>();
singleItemStack.Push("Item");
emptyStack = new Stack<string>();
}
[TestMethod]
public void TestPush()
{
emptyStack.Push("Added");
Assert.AreEqual(1, emptyStack.Count);
}
[TestMethod]
public void TestRemove()
{
singleItemStack.Pop();
Assert.AreEqual(0, singleItemStack.Count);
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void TestPopFromEmpty()
{
emptyStack.Pop();
}
}
If you need to have some item added before testing removal, then best place to add item is arrange part of removal test. This will make context of removal test clear.
But DRY principle also works here - you can move addition logic to separate helper method. Then call it twice - when testing addition, and when arranging context for removal:
[Testclass]
public class Tests
{
[TestMethod]
public void TestAddition()
{
AddItem();
// Assert addition
}
[TestMethod]
public void TestRemoval()
{
AddItem();
// Remove item
// Assert removal
}
private void AddItem()
{
// Add item
}
}

Categories

Resources