89

Recently, I've begun to use Moq to unit test. I use Moq to mock out classes that I don't need to test.

How do you typically deal with static methods?

public void foo(string filePath)
{
    File f = StaticClass.GetFile(filePath);
}

How could this static method, StaticClass.GetFile() get mocked?

P.S. I'd appreciate any reading materials you recommend on Moq and Unit Testing.

Chris Shouts
  • 5,377
  • 2
  • 29
  • 40
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384

8 Answers8

56

@Pure.Krome: good response but I will add a few details

@Kevin: You have to choose a solution depending on the changes that you can bring to the code.
If you can change it, some dependency injection make the code more testable. If you can't, you need a good isolation.
With free mocking framework (Moq, RhinoMocks, NMock...) you can only mock delegates, interfaces and virtual methods. So, for static, sealed and non-virtual methods you have 3 solutions:

  • TypeMock Isolator (can mock everything but it's expensive)
  • JustMock of Telerik (new comer, less expensive but still not free)
  • Moles of Microsoft (the only free solution for isolation)

I recommend Moles, because it's free, efficient and use lambda expressions like Moq. Just one important detail: Moles provide stubs, not mocks. So you may still use Moq for interface and delegates ;)

Mock: a class that implements an interface and allows the ability to dynamically set the values to return/exceptions to throw from particular methods and provides the ability to check if particular methods have been called/not called.
Stub: Like a mock class, except that it doesn't provide the ability to verify that methods have been called/not called.

Jeco
  • 1,080
  • 8
  • 11
  • 10
    As an update to this. Moles is now called Fakes and is built into Visual Studio 2012: http://msdn.microsoft.com/en-us/library/hh549175.aspx – gscragg Nov 27 '13 at 21:55
  • 2
    In Moles you can verify if a method has been called just adding an boolean variable and setting it true inside of the stub function also can verify all the parameters in the call with this workarround. – mart Dec 20 '13 at 13:28
  • 3
    Your definition of mocks and stubs is more complicated than necessary. A mock is a fake object that you assert that some behavior has happened with. A stub is a fake object that is only used to provide "canned" data to the test. Stubs are never asserted against. In short. mocks are about behavior and stubs are about state. – Scott Marcus Oct 14 '15 at 19:58
  • There is an open source alternative to Microsoft Fakes (that is only available in premium/ultimate editions of visual studio) called Prig (PRototyping jIG): https://github.com/urasandesu/Prig. It's available as both nuget and downloadable binaries, and as long as you don't compile the source, it can be used in less premium editions of visual studio. – Anders Asplund May 10 '17 at 20:27
  • 2
    Microsoft.Fakes isn't "free", as it isn't available in the VS Community edition. – Crono Nov 07 '18 at 19:12
37

Mocking frameworks like Moq or Rhinomocks can only create mock instances of objects, this means mocking static methods is not possible.

You can also search Google for more info.

Also, there's a few questions previously asked on StackOverflow here, here and here.

Community
  • 1
  • 1
Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
21

There is a possibility in .NET excluding MOQ and any other mocking library. You have to right click on solution explorer on assembly containing static method you want to mock and choose Add Fakes Assembly. Next you can freely mock that assembly static methods.

Assume that you want to mock System.DateTime.Now static method. Do this for instance this way:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => new DateTime(1837, 1, 1);
    Assert.AreEqual(DateTime.Now.Year, 1837);
}

You have similar property for each static property and method.

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
pt12lol
  • 2,332
  • 1
  • 22
  • 48
  • 14
    A warning to people finding this : MS Fakes is built-in in Visual Studio 2012+ Premium/Ultimate only. You will probably not be able to integrate it into a continuous integration chain. – thomasb Jun 05 '15 at 16:26
  • 3
    This should be the accepted answer. MS Fakes with shims now makes it very possible to mock static methods. – Jez Mar 10 '16 at 11:47
  • 2
    Be VERY careful using MS Fakes! Fakes is a redirection framework, and its use quickly leads to poor architecture. Only use Fakes when absolutely necessary, to intercept calls to 3rd party or (very) legacy libraries. The best solution is to create a class wrapper with an interface, and then pass the interface in through constructor injection or via an injection framework. Mock the interface, and then pass that into the code under test. Stay away from Fakes, if at all possible. – Mike Christian Jan 17 '17 at 17:53
11

You can achieve this with Pose library available from nuget. It allows you to mock, among other things, static methods. In your test method write this:

Shim shim = Shim.Replace(() => StaticClass.GetFile(Is.A<string>()))
    .With((string name) => /*Here return your mocked value for test*/);
var sut = new Service();
PoseContext.Isolate(() =>
    result = sut.foo("filename") /*Here the foo will take your mocked implementation of GetFile*/, shim);

For further reading refer here https://medium.com/@tonerdo/unit-testing-datetime-now-in-c-without-using-interfaces-978d372478e8

mr100
  • 4,340
  • 2
  • 26
  • 38
  • Very promising, although when I tried to use it I learned that it does not yet support extension methods. Hopefully that it will include that soon. – Samer Adra Aug 09 '18 at 18:56
  • I am impressed by it too. But it works for extension methods already, just tested it! You just mock extenstion method like it is an ordinary static method: Shim shim = Shim.Replace(() => StaticClass.GetSomeNumber(Is.A())).With((int value) => 2); In this case GetSomeNumber is an extension method and so it is used in code: var number = 3.GetSomeNumber(); And this works! – mr100 Aug 10 '18 at 05:31
  • This has of course limitations like I could not get it to work with generic static methods. But for sure extension methods are no problem. – mr100 Aug 10 '18 at 05:32
  • Pose hasn't been updated in a while and there are a number of issues reported with it that make it not a good candidate right now. – David Clarke May 21 '20 at 23:20
3

I liked Pose but couldn't get it to stop throwing InvalidProgramException which appears to be a known issue. Now I'm using Smocks like this:

Smock.Run(context =>
{
    context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));

    // Outputs "2000"
    Console.WriteLine(DateTime.Now.Year);
});
sirdank
  • 3,351
  • 3
  • 25
  • 58
2

I've been playing around with a concept of refactoring the static methods to invoke a delegate which you can externally set for testing purposes.

This would not use any testing framework and would be a completely bespoke solution however the refactor will not influence the signature of your caller and so it would be a relatively safe.

For this to work, you would need to have access to the static method, so it wouldn't work for any external libraries such as System.DateTime.

Heres an example I've been playing with where I've created a couple of static methods, one with a return type that takes in two parameters and one generic which has no return type.

The main static class:

public static class LegacyStaticClass
{
    // A static constructor sets up all the delegates so production keeps working as usual
    static LegacyStaticClass()
    {
        ResetDelegates();
    }

    public static void ResetDelegates()
    {
        // All the logic that used to be in the body of the static method goes into the delegates instead.
        ThrowMeDelegate = input => throw input;
        SumDelegate = (a, b) => a + b;
    }

    public static Action<Exception> ThrowMeDelegate;
    public static Func<int, int, int> SumDelegate;

    public static void ThrowMe<TException>() where TException : Exception, new()
        => ThrowMeDelegate(new TException());

    public static int Sum(int a, int b)
        => SumDelegate(a, b);
}

The Unit Tests (xUnit and Shouldly)

public class Class1Tests : IDisposable
{
    [Fact]
    public void ThrowMe_NoMocking_Throws()
    {
        Should.Throw<Exception>(() => LegacyStaticClass.ThrowMe<Exception>());
    }

    [Fact]
    public void ThrowMe_EmptyMocking_DoesNotThrow()
    {
        LegacyStaticClass.ThrowMeDelegate = input => { };

        LegacyStaticClass.ThrowMe<Exception>();

        true.ShouldBeTrue();
    }

    [Fact]
    public void Sum_NoMocking_AddsValues()
    {
        LegacyStaticClass.Sum(5, 6).ShouldBe(11);
    }

    [Fact]
    public void Sum_MockingReturnValue_ReturnsMockedValue()
    {
        LegacyStaticClass.SumDelegate = (a, b) => 6;
        LegacyStaticClass.Sum(5, 6).ShouldBe(6);
    }

    public void Dispose()
    {
        LegacyStaticClass.ResetDelegates();
    }
}
Joe_DM
  • 985
  • 1
  • 5
  • 12
1

Using Microsoft Fakes:

Add the fakes assembly, then if you have this static method...

//code under test
public static class MyClass {
    public static int MyMethod() {
        ...
    }
}

... you can mock it like this:

// unit test code
using (ShimsContext.Create())
{
    ShimMyClass.MyMethod = () => 5;
}

Source: https://learn.microsoft.com/en-us/visualstudio/test/using-shims-to-isolate-your-application-from-other-assemblies-for-unit-testing?view=vs-2019#static-methods

Telmo Marques
  • 5,066
  • 1
  • 24
  • 34
0

Now you can try my free lib YT.IIGen. It allows to generate an interface for a static class and an implementaion wrapper.

using YT.IIGen.Attributes;

[IIFor(typeof(StaticClass), "StaticClassWrapper")]
internal partial interface IStaticClass
{
}