/MockableStaticGenerator

A C# source generator to make an interface and a class wrapper to test static and extension methods.

Primary LanguageC#MIT LicenseMIT

encode

Problem

I want to mock static or extensions methods. What should I do?

Sometimes I have those static methods inside my library and sometime they are in an external library like Dapper

Do you have any solution?

Solution

A good, well-known solution is writing a wrapper.

Consider the following example:

public class MyClass
{
    public void LogMessage(string message)
    {
        Logger.Write(message);
    }
}

public class Logger
{
    public static void Write(string message)
    {
       //Write your code here to log data
    }
}

You can use Moq to mock non-static methods but it cannot be used to mock static methods, so what should I do if I want to mock Logger.Write?

Create a wrapper class and interface

Make an interface just like the signature of the static method

public interface IWrapper
{
    void LogData(string message);
}

Implement that interface and call the static method (Logger.Write) into the same non-static method.

public class LogWrapper : IWrapper
{
    string _message = null;
    public LogWrapper(string message)
    {
        _message = message;
    }
    public void LogData(string message)
    {
        _message = message;
        Logger.Write(_message);
    }
}

Now you can use IWrapper and LogWrapper everywhere in your code and also make things mockable.

var mock = new Mock<IWrapper>();
mock.Setup(x => x.LogData(It.IsAny<string>()));
new ProductBL(mock.Object).LogMessage("Hello World!");
mock.VerifyAll();

What is the wrong part with this solution?

Of course, you can not do this for all static methods or for all external libraries. It is hard and tedious work. Maybe sometimes impossible!

MockableStaticGenerator

MockableStaticGenerator is a C# code generator for making your static/extension methods even from an external library mockable just like above approach but automatically!

How it works?

  • For internal usage

If you have a class with static methods inside it, put [MockableStatic] on top of the class

// For your class with some static methods.
[MockableStatic]
public class Math
{
    public static int Add(int a, int b) { return a+b; }
    public static int Sub(int a, int b) { return a+b; }
}
  • For external usage

If you have a class with static methods inside it but from somewhere else (an external library for example), you should introduce type of that class to the [MockableStatic]

// For type of an external assembly with a lot of static methods
// like 'Dapper.SqlMapper' class in Dapper external library.
// It does not matter on top of what class it is, but Program class can be appropriate.
[MockableStatic(typeof(Dapper.SqlMapper))]
class Program
{
   static void Main(string[] args)
   {
       // ...
   }
}

How to call the generated interfaces and classes?

There are two naming conventions you should follow:

  • I{CLASS_NAME}Wrapper for the interface
  • {CLASS_NAME}Wrapper for the class

and you can find them under this namespace.

  • namespace MockableGenerated
    • MockableGenerated.I{CLASS_NAME}Wrapper
    • MockableGenerated.{CLASS_NAME}Wrapper

image image

Attention

You must use the generated interfaces and classes instead of the originals to make your library/application testable and mockable.


Open Source Love Nuget Nuget

Install-Package MockableStaticGenerator
dotnet add package MockableStaticGenerator

For more details read this blog post.


Icons made by Freepik from www.flaticon.com