Pixel Lord
Pixel Lord

Reputation: 145

Trying to Test a Controller with a UrlHelper

Trying to create a URLHelper for testing purposes throws a NullReferenceException.

Example:

[Fact]
public async void AuthenticateAsyncTest()
{
  // Arrange
  var controller = new Controller(serviceProvider)
  {
    Url = new UrlHelper(new ActionContext()) // Exception thrown
  };

  // Act
  var result = await controller.Authenticate() as ViewResult;

  // Assert
  Assert.NotNull(result);
}

Every time I run this Test, the Exception that is thrown in Url = new UrlHelper(new ActionContext()) is:

Exception.Message:

Message: System.NullReferenceException : Object reference not set to an instance of an object.

Exception.StackTrace:

UrlHelperBase.ctor(ActionContext actionContext) ControllerUnitTest.AuthenticateAsyncTest()

Using:

xUnit 2.4.1, Microsoft.NETCore.App 2.2.0, Microsoft.AspNetCore.Routing.Abstractions 2.2.0

To recreate the Exception:

  1. Create a empty MVC core 2.2 solution
  2. Create a xunit test Project
  3. Install the NuGet Microsoft.AspNetCore.Mvc.Core 2.2.0
  4. Write in the test: var Url = new UrlHelper(new ActionContext());
  5. Run test

Should look like this:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Xunit;

namespace XUnitTestProject1
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            var Url = new UrlHelper(new ActionContext());
        }
    }
}

My questions:

  1. Is there a bug, or why is this not working?
  2. Literature to a workaround or links are appreciated?

Upvotes: 6

Views: 2945

Answers (2)

Pixel Lord
Pixel Lord

Reputation: 145

The second option would be to use the specific constructor. The document states that it should be used for unit testing, more specifically when the ActionContext simply needs to be passed in, but not used by the consuming code.

UrlHelper Url = new UrlHelper(new ActionContext { RouteData = new RouteData() });

Thank you to navelDirt and pranavkm who replayed on githhub: https://github.com/aspnet/AspNetCore/issues/6703

Upvotes: 2

Nkosi
Nkosi

Reputation: 247068

According to GitHub source code referred to by the exception message,

protected UrlHelperBase(ActionContext actionContext)
{
    if (actionContext == null)
    {
        throw new ArgumentNullException(nameof(actionContext));
    }

    ActionContext = actionContext;
    AmbientValues = actionContext.RouteData.Values;
    _routeValueDictionary = new RouteValueDictionary();
}

The helper is trying to access actionContext.RouteData.Values which was not provided in the original example.

Provide the necessary dependencies for the test to flow to completion.

[Fact]
public async Task AuthenticateAsyncTest() {
    // Arrange
    var httpContext = new DefaultHttpContext();
    var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
    var controller = new Controller(serviceProvider) {
        Url = new UrlHelper(actionContext)
    };

    // Act
    var result = await controller.Authenticate() as ViewResult;

    // Assert
    Assert.NotNull(result);
}

Also avoid using async void for unit tests. Use Task instead.

Upvotes: 5

Related Questions