Mr.Rendezvous
Mr.Rendezvous

Reputation: 1993

How can I mock this static method

I have this code:

public static bool IsValidVoucher(string id)
{
    //read tsv files
    var temp1 = AppData.GetAppData("stringval");            
    // code that need to be tested        
    return true;        
}

And I like to create unit test, but how can I mock AppData.GetAppData("stringval") to whatever I want the value is, so I can test the rest of code.

The AppData class is:

public class AppData
{
    public static object GetAppData(string name)
    {
        //...
    }
}

Upvotes: 13

Views: 27555

Answers (2)

code4life
code4life

Reputation: 15794

Well, I think everyone's comments so far is technically correct - using something like RhinoMocks or Moq, you really can't mock static methods in a facile, straightforward manner.

But using Moles, you definitely can. So if you have significant (currently) untestable code that reside within static methods, I think you should be looking into Moles.

(This link is a bit dated but I still find it helpful) http://research.microsoft.com/en-us/projects/pex/molesmanual.pdf

(Key text)

Moles can be used to detour any .NET method, including non-virtual and static methods in sealed types.

How it works: Suppose you have a typical situation like this:

public static class SomeStaticClass
{
    public static int SomeStaticMethod(string s)
    {
        return "Static method called: " + s;
    }
}

public class SomeInstanceClass
{
    public string SomeInstanceMethod(string s)
    {
        return SomeStaticClass.SomeStaticMethod(s);
    }
}

Using Moles, your test code would look like this:

[TestMethod()]
[HostType("Moles")]
public void ShouldBeAbleToTestStaticMethod()
{
    var instance = new SomeInstanceClass();
    var testValue = instance.SomeInstanceMethod("Some test string");
    SomeStaticClass.SomeStaticMethod = (s) => "Moled you! " + s;
    Assert.That(testValue, Is.EqualTo("Moled you! Some test string"); // sorry, this has code smell, lol
}

Of course you need to set up Moles into your test project, so be sure to look it up - lots of web resources to help you on your way.

Some helpful posts:

https://msdn.microsoft.com/en-us/library/ff798308.aspx

http://adventuresdotnet.blogspot.com/2011/03/mocking-static-methods-for-unit-testing.html

https://wannabeegeek.wordpress.com/2013/03/13/unit-testing-made-easy-with-moles-part-i/

Upvotes: 1

rexcfnghk
rexcfnghk

Reputation: 15452

Static methods cannot be mocked in an easy way. You basically have two choices:

  1. If you own the AppData class, change the implementation to implement an interface (e.g. IAppData) and remove the static modifier on the GetAppData method so you can mock it.

    public interface IAppData
    {
        object GetAppData(string id);
    }
    
    public class AppData : IAppData
    {
        public object GetAppData(string id) {}
    }
    
    public class Foo
    {
        private readonly IAppData _appData;
    
        public Foo(IAppData appData)
        {
            _appData = appData;
        }
    
        public bool IsValidVoucher(string id)
        {
            // Call through object instance instead for class reference
            var temp1 = _appData.GetAppData("stringval");
        }
    }
    
  2. If you do not own the AppData class, use a wrapper class (e.g. AppDataWrapper) that implements an interface and call that method from IsValidVoucher instead:

    public interface IAppData
    {
        object GetAppData(string id);
    }
    
    public class AppDataWrapper : IAppData
    {
        public object GetAppData(string id)
        {
            return AppData.GetAppData(id);
        }
    }
    
    public class Foo
    {
        private readonly IAppData _appData;
    
        public Foo(IAppData appData)
        {
            _appData = appData;
        }
    
        public bool IsValidVoucher(string id)
        {
            var temp1 = _appData.GetAppData("stringval");
        }
    }
    

You can then unit test Foo using Moq (using xunit as an example here):

public class FooTests
{
    private readonly IAppData _mockAppData;

    public FooTests()
    {
        var mockAppData = new Mock<IAppData>();
        mockAppData.Setup(m => m.GetAppData(It.IsAny<string>)).Returns("my test value");
        _mockAppData = mockAppData.Object;
    }

    [Fact]
    public void IsValidVoucher_ValidAppData_Returns()
    {
        var foo = new Foo(_mockAppData);
        // Unit test foo.IsValidVoucher
    }
}

Upvotes: 12

Related Questions