关于asp.net mvc:Moq-如何模拟HttpContext.Request.Url.Authority对象之类的非虚拟成员?

Moq - how can I mock a non-virtual member like HttpContext.Request.Url.Authority object?

我似乎无法使用Moq来模拟HttpContext.Request.Url.Authority,因为它是一种非虚拟方法。 我得到以下异常:

1
{"Invalid setup on a non-virtual (overridable in VB) member: p => p.HttpContext.Request.Url.Authority"}

我该如何克服呢? 以下是我的测试方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[TestMethod]
public void ForgottenPasswordPost_Requested_CaptchaCorrectEmailExists()
{
    _testModel.ControllerContext.SetupGet(p => p.HttpContext.Session["Captcha"]).Returns("HelloWorld");
    _testModel.ControllerContext.SetupGet(p => p.HttpContext.Request.Url.Authority).Returns("www.localhost.com");
    _testModel.QMember.Setup(m => m.MemberExistsWithEmail(It.IsAny<string>())).Returns(true);


    var controllerUnderTest = _testModel.ReturnController();
    ForgottenPasswordModel model = new ForgottenPasswordModel() { Captcha ="HelloWorld" };

    //Act
    var actionResult = (RedirectToRouteResult)controllerUnderTest.ForgottenPassword(model);

    Assert.AreEqual("ForgottenPasswordConfirm", actionResult.RouteValues["action"]);
    Assert.AreEqual("a", actionResult.RouteValues["controller"]);
}

public class TestModel
{
    public UnregisteredController Controller { get; set; }
    public Mock<ControllerContext> ControllerContext { get; set; }
    public Mock<IQ_Member> QMember { get; set; }

    public TestModel()
    {
        ControllerContext = new Mock<ControllerContext>();
        QMember = new Mock<IQ_Member>();
    }

    public UnregisteredController ReturnController()
    {
        Controller = new UnregisteredController(QMember.Object);
        Controller.ControllerContext = ControllerContext.Object;
        return Controller;
    }
}


根据此SO帖子,您只能使用Moq来实现虚拟/抽象成员。

在非ASP.NET MVC的一般情况下,您可以

  • 使用模拟工具,该工具将允许覆盖非虚拟的,具体的类成员,或者

  • 使用传递方法向静态/非虚拟成员编写您自己的包装器类,从这些包装器类中提取接口,然后将其余代码设计为依赖于这些接口,然后可以轻松对其进行模拟并注入依赖项。

  • 我建议在上面的#1上使用#2,尽管它会导致大量的工作,因为#2几乎可以肯定会导致代码更好地遵循依赖倒置原则。

    但是,在您的情况下,我认为有一个相当简单的解决方案。

    在代码行中

    1
    _testModel.ControllerContext.SetupGet(p => p.HttpContext.Request.Url.Authority).Returns("www.localhost.com");
    • .HttpContext是抽象的,因此可以被嘲笑
    • .Request是抽象的,因此可以被嘲笑
    • .Url是虚拟的,因此可以被嘲笑
    • .Authority是非虚拟字符串属性,这就是给您带来问题的原因

    请尝试以下操作,因为.Url是您可以模拟的最深部分:

    1
    _testModel.ControllerContext.SetupGet(p => p.HttpContext.Request.Url).Returns(new Uri("http://www.localhost.com"));