Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wrapping a static class/method in order to unit test it?

I've got a static class that I am using for logging:

public static class myLogger
{
    public static ErrorLogging(string input)
    {
        //dostuff
    }
}

The way I am using it is:

public class myClassthatDoesStuff
{
    ...
    myLogger.ErrorLogging("some error ocurred");
    ...
}

How can I moq the myLogger class in order to be able to unit test it and ensure that the ErrorLogging method was executed? Is it possible to do this without setting any parameters in the constructor (constructor injection)? myClassthatDoesStuff requires that there are no parameters in the constructor.

like image 956
Alex Gordon Avatar asked Mar 06 '16 14:03

Alex Gordon


People also ask

Can a static method be unit tested?

A static method that holds no state or doesn’t change state can be unit tested. As long as the method and its dependencies are idempotent, the method can be unit tested. The problems arise when the static method calls other methods or when the object being tested calls the static method.

What does the static method of a class hold?

The static method holds state information, i.e., if it caches data into a static object of the class. Consider the following code snippet that shows two classes, namely ProductBL and Logger.

How do I wrap a static class with an interface?

Wrap the static class with an instance class and delegate calls: We can create an interface that expose all the functionality of the static class. Then all we need to do is wrap it with a simple class that will delegate the calls to the static class: // the interface should look exactly like the static class we want to wrap !

Is it possible to unit test a method?

As long as the method and its dependencies are idempotent, the method can be unit tested. The problems arise when the static method calls other methods or when the object being tested calls the static method. On the other hand, if the object being tested calls an instance method, then you can unit test it easily.


2 Answers

This blog post describes the exact same scenario - you have an old static logging method and want to use it in testable code.

  • Wrap the static class in a non-static class - not just for testing, but for general use.
  • Extract the methods of your new non-static class into an interface.
  • Wherever you would have depended on the static class, depend on the interface instead. For example, if class DoesSomething requires the function in your static class, do this:

    public interface ILogger
    {
        void ErrorLogging(string input);
    }
    
    public class MyClassthatDoesStuff
    {
        private readonly ILogger _logger;
    
        public MyClassthatDoesStuff(ILogger logger)
        {
            _logger = logger;
        }
    }
    

This gives you two benefits:

  1. You can unit test your old static class (assuming that it has no state and doesn't depend on anything that has any state) (although if that's the case I suppose you could unit test it anyway.)
  2. You can unit test code that will use that static class (by removing the direct dependency on that static class.) You can replace ILogger with a mocked class, like one that adds your error messages to a list.

    class StringLogger : List<string>, ILogger
    {
        public void ErrorLogging(string input)
        {
           Add(input);
        }
    }
    
    var testSubject = new MyClassthatDoesStuff(new StringLogger());
    
like image 54
Scott Hannen Avatar answered Oct 20 '22 08:10

Scott Hannen


If you can not change it from a static to a non-static class, wrap it with a non-static class...

void Test()
{
    string testString = "Added log";
    var logStore = new List<string>();
    ILogger logger = new MyTestableLogger(logStore);

    logger.ErrorLogging(testString);

    Assert.That(logStore.Any(log => log==testString));
}

public interface ILogger
{
    void ErrorLogging(string input);
}

public class MyTestableLogger : ILogger
{
    public MyTestableLogger(ICollection<string> logStore)
    {
        this.logStore = logStore;
    }

    private ICollection<string> logStore;

    public void ErrorLogging(string input)
    {
        logStore.Add(input);
        MyLogger.ErrorLogging(input);
    }
}

public static class MyLogger
{
    public static void ErrorLogging(string input)
    {
        // Persist input string somewhere
    }
}
like image 45
Aydin Avatar answered Oct 20 '22 08:10

Aydin