5

I have an MVC6 controller with the following line of code:

string link = Url.Action("Profile", "Account");

When I unit test this controller, that line fails with an error:

Value cannot be null.
Parameter name: helper

I don't really want to mock the Url.Action response plus I don't think I can because it's an extension method (static method). I want it to run and return like it would in a web environment.

What do I need to do when instantiating the controller in my unit test so that I the line above will execute as expected?

I see that I can do something like this in my unit test:

controller.Url = new UrlHelper(new ActionContextAccessor(), new DefaultActionSelector(...));

But I can't figure out what is needed to setup the ActionContextAccessor and/or the DefaultActionSelector (which requires more types that I'm not sure where to obtain or how to instantiate).

Has anyone already done this?

Thanks, Kevin

retsvek
  • 385
  • 4
  • 15

2 Answers2

8

My wife always tells me to just "walk away" from a problem when I'm stuck. And she's almost always right.

After realizing the solution above wasn't going to work (because it's MVC5), I took another look and realized that controller.Url is just an instance of IUrlHelper. I mocked that and poof the tests started to work. Here's the gist of what I'm doing. This made the test able to execute. I'm sure I can add some more to the mocking to actually verify it's functioning as expected.

        Mock<IUrlHelper> urlHelperMock = new Mock<IUrlHelper>();
        var controller = new BooksController();
        controller.Url = urlHelperMock.Object;

Yeah, it was that easy LOL.

Thanks, Kevin

retsvek
  • 385
  • 4
  • 15
0

Given:

namespace SampleWebApplication.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            string link = Url.Action("Profile", "Account");
            return View();
        }
    }
}

This test seems to run through OK:

using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SampleWebApplication.Controllers;

namespace SampleUnitTestProject
{
    [TestClass]
    public class HomeTests
    {
        private Mock<HttpRequestBase> RequestMock;
        private Mock<HttpResponseBase> ResponseMock;
        private Mock<HttpContextBase> ContextMock;

        [TestInitialize]
        public virtual void Setup()
        {
            this.RequestMock = new Mock<HttpRequestBase>();
            this.ResponseMock = new Mock<HttpResponseBase>();
            this.RequestMock.Setup(m => m.QueryString).Returns(new NameValueCollection());
            this.RequestMock.Setup(m => m.Url).Returns(new Uri("http://www.somedomain.com"));
            this.ContextMock = new Mock<HttpContextBase>();

            this.ContextMock.Setup(m => m.Request).Returns(this.RequestMock.Object);
            this.ContextMock.Setup(m => m.Response).Returns(this.ResponseMock.Object);
        }

        [TestMethod]
        public void Test_Index()
        {
            // Arrange

            using (var controller = new HomeController())
            {
                this.RequestMock.Setup(c => c.ApplicationPath).Returns("/tmp/testpath");
                this.ResponseMock.Setup(c => c.ApplyAppPathModifier(It.IsAny<string>())).Returns("/mynewVirtualPath/");
                var requestContext = new RequestContext(this.ContextMock.Object, new RouteData());
                controller.Url = new UrlHelper(requestContext, new RouteCollection());

                // Act
                var result = controller.Index();

                // Assert
            }
        }
    }
}
NikolaiDante
  • 18,469
  • 14
  • 77
  • 117