【问题标题】:Moq - how can I mock a non-virtual member like HttpContext.Request.Url.Authority object?Moq - 我如何模拟像 HttpContext.Request.Url.Authority 对象这样的非虚拟成员?
【发布时间】:2013-01-22 15:18:48
【问题描述】:

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

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

我该如何克服这个问题?以下是我的测试方法:

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

【问题讨论】:

    标签: asp.net-mvc tdd moq


    【解决方案1】:

    根据this SO post,只能使用Moq实现虚拟/抽象成员。

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

    1. 使用允许覆盖非虚拟的具体类成员或

    2. 的模拟工具
    3. 编写您自己的封装类,使用传递方法传递给静态/非虚拟成员,从这些封装类中提取接口,然后设计其余代码以依赖这些接口,这样就可以轻松被嘲笑和依赖注入。

    我会推荐 #2 而不是上面的 #1,即使它会产生相当多的工作,因为 #2 几乎肯定会产生更好地遵守 Dependency Inversion principle 的代码。

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

    在代码行中

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

    试试这个,因为.Url 是你可以模拟的最深的部分:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-27
      • 1970-01-01
      • 2017-03-29
      • 1970-01-01
      • 2013-12-17
      • 1970-01-01
      • 2016-04-20
      • 2012-07-29
      相关资源
      最近更新 更多