Using mock to test a void method

80 views Asked by At

I have a class

public class NLogRuleComposer : INLogComponentComposer
{
    private LoggingConfiguration _nLogLoggingConfiguration;

    //TODO:  think we need to constructor-inject the logging config 
    public void ComposeComponent(LoggerModel loggerModel
        , LoggingConfiguration nLogLoggingConfiguration
        , string loggerFullName)
    {
        _nLogLoggingConfiguration = nLogLoggingConfiguration;
        var ruleName = loggerFullName;
        var minimumLevel = LogLevel.FromString(loggerModel.level.min_level);
        var maximumLevel = LogLevel.FromString(loggerModel.level.max_level);
        var allTargetsWithLayout = nLogLoggingConfiguration.AllTargets.OfType<TargetWithLayout>().ToList();
        var targetWithoutStackTrace = allTargetsWithLayout
            .FirstOrDefault(x => x.Name.Contains("WithoutStacktrace"));
        var targetWithStackTrace = allTargetsWithLayout.FirstOrDefault(x => x.Name.Contains("WithStacktrace"));
        CreateDefaultLevelFilter(ruleName, maximumLevel.Name, minimumLevel, targetWithoutStackTrace); 
    }

    public void CreateDefaultLevelFilter(string ruleName, string maximumLevelName
        , LogLevel minimumLevel, TargetWithLayout targetWithoutStackTrace)
    {
        var filter = new ConditionBasedFilter();
        filter.Action = FilterResult.Log;
        filter.Condition = "(level <= LogLevel." + maximumLevelName + ")";
        var loggerRule = new LoggingRule(ruleName, targetWithoutStackTrace);
        loggerRule.Filters.Add(filter);
        _nLogLoggingConfiguration.LoggingRules.Add(loggerRule);
    }

Now I want to test CreateDefaultLevelFilter method, but it is void. I don't know how to test it and test what. Using Mock maybe?

Unfinished code:

public class NLogRuleComposerUnitTests
{
    [Theory]
    [InlineData("MyLoggerName")]
    public void NLogRuleComposer_Should_Create_A_LoggingRule(
        string expectedLogger)
    {
        var nLogRuleComposerMock = new Mock<INLogComponentComposer>();
        var nLogTargetMock = new Mock<TargetWithLayout>();
        nLogRuleComposerMock.Setup(x => x.ComposeComponent(It.IsAny<LoggerModel>()
    , It.IsAny<LoggingConfiguration>()
    , It.IsAny<string>()))
    .Verifiable();
    }
}
1

There are 1 answers

2
Claudio Redi On BEST ANSWER

You'll be in troubles to test that piece of code without some dependency injection. Creating instances like this

var filter = new ConditionBasedFilter();

it make impossible to unit test since, as side effect, you're also testing class ConditionBasedFilter. Your unit test, ideally, should not test anything except method result or behavior. External dependencies should be mocked.

What I would do:

  • Create factories to build instances of all related classes.
  • Inject factories interfaces on constructor
  • Mock factories to have control about what they return so you can validate, for instance, that correct information is set to filter

If you don't have the habit of inject dependencies, you should start making this habit :) Without that, it's impossible to do unit test properly.

This is an example of how you could test ConditionBasedFilter properties

var conditionBasedFilter = new ConditionBasedFilter();
var conditionBasedFilterFactoryMock = new Mock<IConditionBasedFilterFactory>
var conditionBasedFilterFactoryMock
     .Setup(f => Create())
     .Returns(conditionBasedFilter);

Then, you use the factory like this inside your CreateDefaultLevelFilter method

// this factory is injected on contructor
var filter =  _conditionBasedFilterFactory.Create(); 

At the end of your test, you assert that filter was set with correct values for Action and Condition. Similar applies for loggerRule