52

I am pretty new to use moq. I am into creating some unit test case to HttpModule and everything works fine until I hit a static property as follows

this.applicationPath = (HttpRuntime.AppDomainAppVirtualPath.Length > 1) ? HttpRuntime.AppDomainAppVirtualPath : String.Empty;

I do not know how create mocks for static class and property like HttpRuntime.AppDomainAppVirtualPath. The context, request and response have been mocked well with sample code I get from moq. I will appreciate if somebody can help me on this.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
jyothish george
  • 521
  • 1
  • 4
  • 3

6 Answers6

73

Moq can't fake static members.

As a solution you can create a wrapper class (Adapter Pattern) holding the static property and fake its members.
For example:

public class HttpRuntimeWrapper
{
    public virtual string AppDomainAppVirtualPath 
    { 
        get
        { 
            return HttpRuntime.AppDomainAppVirtualPath; 
        }
    }
}

In the production code you can access this class instead of HttpRuntime and fake this property:

[Test]
public void AppDomainAppVirtualPathTest()
{
    var mock = new Moq.Mock<HttpRuntimeWrapper>();
    mock.Setup(fake => fake.AppDomainAppVirtualPath).Returns("FakedPath");

    Assert.AreEqual("FakedPath", mock.Object.AppDomainAppVirtualPath);
}

Another solution is to use Isolation framework (as Typemock Isolator) in which you can fake static classes and members.
For example:

Isolate.WhenCalled(() => HttpRuntime.AppDomainAppVirtualPath)
       .WillReturn("FakedPath");

Disclaimer - I work at Typemock

Elisha
  • 23,310
  • 6
  • 60
  • 75
  • 6
    When asking about Moq, suggesting a comerical product is a bit off. – Finglas Mar 10 '10 at 11:34
  • 42
    Why? It's an option. Wrapping static is better, but knowing your options is always a good thing. – Krzysztof Kozmic Mar 10 '10 at 11:37
  • 2
    He's asking for a solution on how to do this with Moq. That's why. – Finglas Mar 10 '10 at 11:38
  • No it doesn’t seem to be replaced HttpRuntime.AppDomainAppVirtualPath. The test crashes at this call. Any inputs? – jyothish george Mar 10 '10 at 12:20
  • MSR's Moles offers functuionaly in this space too. – Ruben Bartelink Mar 10 '10 at 13:48
  • @jyothish george, have you replaced the call in the production code to the new wrapper? In the production code instead of static call on HttpRuntime.AppDomainAppVirtualPath you should have an instance call on httpRuntimeWrapper.AppDomainAppVirtualPath – Elisha Mar 10 '10 at 13:54
  • I changed the static call to HttpRuntimeWrapper wrapper = new HttpRuntimeWrapper(); this.applicationPath = (wrapper.AppDomainAppVirtualPath.Length > 1) ? wrapper.AppDomainAppVirtualPath : String.Empty; My test fixture constructor as follows public UnitTestAspNet() { httpResponse = new Mock(); _httpHttpRuntimeWrapper = new Mock() _httpHttpRuntimeWrapper.Setup(fake => fake.AppDomainAppVirtualPath).Returns("FakedPath"); } But wrapper.AppDomainAppVirtualPath is null while running the test; any idea – jyothish george Mar 11 '10 at 08:46
  • @jyothish george, it should work, I updated the test example, checked it here and it seems to work correctly. Are you passing the mock to your class as an argument? – Elisha Mar 11 '10 at 09:45
  • @ Elisha, I am fairly new to this, sorry if I ask you such a dump question. My understanding is that, when I setup mock.Setup(fake => fake.AppDomainAppVirtualPath).Returns("FakedPath") and this would replace production code with assigned value. This just to test a custom httpmodule and start the init method via testfixture which take a mock application instance as the argument. – jyothish george Mar 11 '10 at 10:20
  • Side note, I modified the production code with wrapper object – jyothish george Mar 11 '10 at 10:22
  • When I use TypeMock Isolator, the statement Isolate.WhenCalled(() => HttpRuntime.AppDomainAppVirtualPath).WillReturn("FakedPath") would automatically replace all the instances of HttpRuntime.AppDomainAppVirtualPath in the production code? In my case, the custom httpmodule use a helper class, and the constructor of this helper class needs application path to be set – jyothish george Mar 11 '10 at 11:44
  • Isolator will automatically replace all the instances of HttpRuntime.AppDomainAppVirtualPath in the production code. Isolator won't force you to change the production code to gain testability in this case. – Elisha Mar 11 '10 at 12:03
  • 13
    +1 for working at TypeMock, and being involved in the community :) – MattDavey Sep 09 '11 at 12:14
17

You cannot Moq static methods with Moq.

This is not a bad thing in reality, static methods and classes do have their place but for logic they make unit testing difficult. Naturally you'll run into them when using other libraries. To get around this you'll need to write an adapter (wrapper) around the static code, and provide an interface. For example:

// Your static class - hard to mock
class StaticClass
{
   public static int ReturnOne() 
   {
       return 1;
   }
}

// Interface that you'll use for a wrapper
interface IStatic
{
    int ReturnOne();
}

Note, I've ommited the concrete class that uses IStatic for the production code. All it would be is a class which uses IStatic and your production code would make use of this class, rather than StaticClass above.

Then with Moq:

var staticMock = new Mock<IStatic>();
staticMock.Setup(s => s.ReturnOne()).Returns(2);
Finglas
  • 15,518
  • 10
  • 56
  • 89
3

As mentioned in previous answers, you can't use MoQ on static methods, and if you need to, your best shot is to create a wrapper around the static class.

However, something I've discovered recently is the Moles project. From the homepage; "Moles allows to replace any .NET method with a delegate. Moles supports static or non-virtual methods." It might be useful for your current situation.

Kirschstein
  • 14,570
  • 14
  • 61
  • 79
  • Interesting, will check it out. – Finglas Mar 10 '10 at 13:29
  • Don't use Moles. First, it adds a lot of headaches and slows down build times dramatically. Second, it's been superceded since this comment was originally posted by Microsoft's newer mocking framework. Third, it's not compatible with VS2012 (which uses the newer framework) and it's a headache here at work because we have a lot of tests written with Moles here that need to be de-Molesified before we can upgrade. Mocking out static methods is tempting (esp. for legacy code), but IMO if you can it's better to just write the code to be more unit-test friendly. – Keith Ripley Dec 12 '12 at 01:11
  • Well of course, but the question concerned the HttpRuntime class which most of us cannot modify, and in those circumstances the need somehow to mock static methods/properties is unavoidable. – haughtonomous Feb 21 '13 at 11:41
2

Best solution I have found so far is Telerik's JustMock - unfortunately only the paid for version allows mocking of statics.

While the idea of wrapping statics is a good one - you can't always do this. If you want to test some code that uses some static classes already then it's not always possible to switch out and use a wrapper. In this case JustMock looks a reasonable solution and I'm probably going to use it on some solutions in the near future.

Ian
  • 33,605
  • 26
  • 118
  • 198
2

You can use Microsoft Fakes for this. It will definitely solve the issue. Refer to https://msdn.microsoft.com/en-us/library/hh549175.aspx

Sujith
  • 1,604
  • 9
  • 16
  • 2
    Although this link might answer the question, it is always good to state the important details also in the question (link could get invalid). – BDL Jul 20 '16 at 13:42
  • microsoft fakes seems to be only available for visual studio enterprise though – gawkface Feb 10 '23 at 20:48
1

Using Microsoft Fakes as suggested by @Sujith is a viable solution. Here is how you actually do it:

  • Find System.Web in your reference of your test project and right click
  • Choose "Add". This adds reference System.Web.4.0.0.0.Fakes
  • Use following code:

    using (Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create()) { System.Web.Fakes.ShimHttpRuntime.AppDomainAppVirtualPathGet = () => "/"; // Do what ever needs the faked AppDomainAppVirtualPath }

Jack Miller
  • 6,843
  • 3
  • 48
  • 66