Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 991
Saket Kumar Avatar asked Dec 10 '22 01:12

Saket Kumar


1 Answers

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);

//...
like image 74
Nkosi Avatar answered Dec 12 '22 14:12

Nkosi