'Error trying to create Mock.Of<ControllerContext>() for ASP.Net Core 3.1 Unit Test

As per last section of the Moq Quickstart defined here, I am trying to configure the following Mock in order to pass Form values to the controller method under test:

var formCollection = new FormCollection(
                new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>()
            {
                    {"mAction", "someAction" },
                    {"mRefId", "0" }
            });

        var controllerContext = Mock.Of<ControllerContext>(ctx =>
            ctx.HttpContext.Request.Form == formCollection);
        
        controller.ControllerContext = controllerContext;

However, when the run the test, it fails on the Mock.Of<> line with the following error:

System.NotSupportedException : Unsupported expression: mock => mock.HttpContext

Non-overridable members (here: ActionContext.get_HttpContext) may not be used in setup / verification expressions.

What am I missing? Am I not doing it the same as per the example defined in the Quickstart document?



Solution 1:[1]

The error is because ControllerContext.HttpContext property is not virtual, so Moq is unable to override it.

Consider using an actual ControllerContext and mocking a HttpContext to assign to the property

var formCollection = new FormCollection(new Dictionary<string, StringValues>()
    {
        {"mAction", "someAction" },
        {"mRefId", "0" }
    });

var controllerContext = new ControllerContext() {
    HttpContext = Mock.Of<HttpContext>(ctx => ctx.Request.Form == formCollection)
};

controller.ControllerContext = controllerContext;

//...

Or even using DefaultHttpContext and assign the desired value(s)

var formCollection = new FormCollection(new Dictionary<string, StringValues>()
    {
        {"mAction", "someAction" },
        {"mRefId", "0" }
    });

HttpContext httpContext = new DefaultHttpContext();
httpContext.Request.Form = formCollection;

var controllerContext = new ControllerContext() {
    HttpContext = httpContext
};

controller.ControllerContext = controllerContext;

//...

Solution 2:[2]

You need to manually set all properties which are not marked virtual either by explicitly setting them in the lambda, or possibly setting them outside (there is no need to create a concrete object), this works by me at least in version 4.16.1.

Method 1 (when using Mock.Of()):

var mockCtx = Mock.Of<ControllerContext>(ctx =>
        ctx.HttpContext == Mock.Of<HttpContext>(hCtx => hCtx.Request.Form 
== formCollection))`

NOTE: In case you need to set all properties to be mocked, or do anything that needs Mock.Get() as in this answer you need to do it directly on the HttpContext as in Mock.Get(mockCtx.HttpContext ) and not on the mockCtx.

Method 2 (when using new Mock<>()):

var mockCtx = new Mock<ControllerContext>();
mockCtx.Object.HttpContext = Mock.Of<HttpContext>(hCtx => hCtx.Request.Form
                                                         == formCollection);

There is no need to create a concrete object, as method 2 is just doing the same but with a mock object.

Note: This will only work if the property is not readonly.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2