使用Moq模拟ControllerBase请求

10

我有一个如下的 .Net Core 2.2 Web API:

[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;
    }
}

我希望在我的单元测试中模拟控制器操作的属性"Scheme"、"Host"和"PathBase"。我已经编写了以下代码:

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

然而,最后一行的模拟会抛出异常,原因是 "ControllerBase" 的 "Request" 无法被重写。我理解抽象类中非虚属性的限制。是否有任何解决方法?

Moq版本为4.13.0。
1个回答

31

改变方法。不要嘲笑测试对象,即本例中的控制器。

可以通过安排测试时设置的HttpContext访问控制器的请求(Request)

例如:

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

//...

2
这太棒了。这正是我在寻找的。 - Rod Talingting
我不得不稍微调整一下才能让它在我的测试中识别主机。它不喜欢http://作为字符串的一部分。这是对我有用的内容。request.Setup(x => x.Host).Returns(HostString.FromUriComponent("localhost:8080")); - ChronoZZ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接