Saket Kumar
Saket Kumar

Reputation: 4825

Mock ControllerBase Request using Moq

I have a Web API in .Net Core 2.2 as below:

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class SomeController : ControllerBase
{
    [HttpPost]
    public async Task<string> SomeMethodPost()
    {
        string returnUrl = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}/some/redirect";

        //Some Third Part Service Call

        return serviceResult;
    }
}

I want to mock the properties "Scheme", "Host" and "PathBase" for my controller action in my unit test. I managed to write below code in my unit test method:

var request = new Mock<HttpRequest>(MockBehavior.Strict);
request.Setup(x => x.Scheme).Returns("http");
request.Setup(x => x.Host).Returns(HostString.FromUriComponent("http://localhost:8080"));
request.Setup(x => x.PathBase).Returns(PathString.FromUriComponent("/api"));

var mockHttp = new Mock<ControllerBase>(MockBehavior.Strict);
mockHttp.SetupGet(x => x.Request).Returns(request.Object);

However, the mock in last line throws exception as "Request" of "ControllerBase" is non overridable. I understand the limitation with non virtual properties of abstract classes. Is there any workaround for this?

Moq version is 4.13.0.

Upvotes: 13

Views: 9934

Answers (1)

Nkosi
Nkosi

Reputation: 246998

Change approach. Do not mock the subject under test, which in this case is the controller.

The controller's Request is accessed via the HttpContext which can be set when arranging the test.

For example

//Arrange
var request = new Mock<HttpRequest>();
request.Setup(x => x.Scheme).Returns("http");
request.Setup(x => x.Host).Returns(HostString.FromUriComponent("http://localhost:8080"));
request.Setup(x => x.PathBase).Returns(PathString.FromUriComponent("/api"));

var httpContext = Mock.Of<HttpContext>(_ => 
    _.Request == request.Object
);

//Controller needs a controller context 
var controllerContext = new ControllerContext() {
    HttpContext = httpContext,
};
//assign context to controller
var controller = new SomeController(){
    ControllerContext = controllerContext,
};

String expected = "expected value here";

//Act
String actual = await controller.SomeMethodPost();


//Assert
Assert.AreEqual(expected, actual);

//...

Upvotes: 34

Related Questions