2

I'm writing unit test using the Moq framework and I have a controller function that returns

return Json(new
{    
    redirectUrl = Url.Action("Action", "Controller"),
    isredirection = true
});

I want to be able to test the result in redirectUrl but don't know how to Mock the Url.Action. How can I setup so i can specify if Url.Action("Action", "Controller") is called return this?

k.m
  • 30,794
  • 10
  • 62
  • 86
user473104
  • 301
  • 2
  • 6
  • 17
  • A common practice is to wrap static classes and extensionmethods like these in an instanced wrapper that's injected into the controller that you can then, in turn, mock. – J. Steen Mar 07 '13 at 10:08
  • https://stackoverflow.com/questions/2387155/unit-test-url-action I used some code from this question. The answer doesn't have any code, but the question shows an example of setting up the Controller.Url property. Url.Action at least did not cause an exception in my unit tests after I followed that example. – 2b77bee6-5445-4c77-b1eb-4df3e5 Apr 23 '19 at 20:56

1 Answers1

1

You can't. There's easy to remember rule about mocking - cannot override, cannot mock1. If you were deriving from Url class, would you be able to override Action method? No. Nor can Moq, Rhino, FakeItEasy or any other framework based on DynamicProxy.

Your options narrow the ones below:

  • use different framework, not based on DynamicProxy (which usually means based on compiler services for advanced interception) - such tools are usually paid.
  • wrap problematic call with interface / delegate and inject it to tested code (so that you can mock it using free framework)

How would the wrapping look like?

public interface IUrlWrapper
{
    string Action(string name, object values);
}

// Wrapper Interface
public class TestedClass
{
    private readonly IUrlWrapper url;

    public TestedClass(IUrlWrapper urlWrapper)
    {
        this.url = urlWrapper;
    }

    // ...

    return Json(new
    {
        redirectUrl = this.url.Action("Action", "Controller"),
        isredirection = true
    });

    // ...
}

With such setup, you can use Moq without further issues. However, in single method call you can just as well use Func delegate without any isolation framework:

// Func Delegate
public class TestedClass
{
    private readonly Func<string, object, string> urlAction;

    public TestedClass(Func<string, object, string> urlAction)
    {
        this.urlAction = urlAction;
    }

    // ...

    return Json(new
    {
        redirectUrl = this.urlAction("Action", "Controller"),
        isredirection = true
    });

    // ...
}

In your test, you simply create delegate on the fly.


1 I wrote a blog post going bit more into details of this very problem: How to mock private method with ...

k.m
  • 30,794
  • 10
  • 62
  • 86