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?
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
?
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();
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
is a C# code generator for making your static/extension methods even from an external library mockable just like above approach but automatically!
- 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)
{
// ...
}
}
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
You must use the generated interfaces and classes instead of the originals to make your library/application testable and mockable.
Install-Package MockableStaticGenerator
dotnet add package MockableStaticGenerator
For more details read this blog post.