Friday, August 11, 2017

Mocking static methods

Problem: How can I mock static methods in unit tests?


Solution
There are two cases:
  1. The static method is in a class that I can change
  2. The static method is in a class that I CANNOT change



Case 1 - Static Method is in a class that I can change


//Code Under Test
public class Foo { public static void Log(string msg) { System.IO.File.WriteAllText("log.txt", msg); } public bool Bar() { Log("Bar was called"); return true; } }

Solution - change method to a delegate, and then set it in the unit test to do nothing

1. Change Log method to a delegate

public class Foo { public static Action<string> Log = (msg) => { System.IO.File.WriteAllText("log.txt", msg); }; public bool Bar() { Log("Bar was called"); return true; } }

2. In the unit test do this
[TestMethod()] public void BarTest() { //Arrange Foo foo = new Foo(); Foo.Log = (msg) => { }; // a function that does nothing bool result = false; //Act result = foo.Bar(); //Assert Assert.IsTrue(result); }


Case 2 - Static Method is in a class that I cannot change

//The Logger class is in some library that I cannot change
    public static class Logger
    {
        public static void Log(string msg)
        {
            System.IO.File.WriteAllText("log.txt", msg);
        }
    }

//Code under test
    public class Foo
    {
        public bool Bar()
        {
            Logger.Log("Bar was called");
            return true;
        }
    }

Solution - wrap call to static method in a virtual method, then mock the wrapper to do nothing

1. Wrap call to static method in a virtual method, change code to call the wrapper

//Code under test
     public class Foo
    {
        public virtual void Log(string msg)
        {
            Logger.Log(msg);
        }
        public bool Bar()
        {
            Log("Bar was called");
            return true;
        }
    }

2. Mock the wrapper so it doesn't do anything
//Note: This is using Moq (https://github.com/Moq/moq4/wiki/Quickstart)

        [TestMethod()]
        public void BarTest()
        {
            //Arrange
            Moq.Mock<Foo> foo = new Moq.Mock<Foo>();
            bool result = false;

            //Act
            result = foo.Object.Bar();

            //Assert
            Assert.IsTrue(result);
        }

//Since Log() is void, and because there are no other virtual methods in the Foo class, i don't need to actually use the Moq Setup() method here.









There was an error in this gadget