【问题标题】:Unit testing Url.IsLocalUrl(returnUrl.ToString()), how can I get it to return false in unit test?单元测试 Url.IsLocalUrl(returnUrl.ToString()),我怎样才能让它在单元测试中返回 false?
【发布时间】:2026-02-18 22:35:01
【问题描述】:

从 MVC3 应用程序中帐户控制器中的标准 LogOn 方法,我该如何测试

Url.IsLocalUrl(returnUrl.ToString()) 

url 不是本地的代码行?换句话说,在单元测试时我必须在这行代码中输入什么 url,才能让它返回 false?

我使用了以下想法,这将返回为 false(非本地):

Uri uri = new Uri(@"http://www.google.com/blahblah.html");

但它只是在单元测试中抛出了一个空异常

编辑:我应该补充一点,LogOn 方法现在看起来像这样:

public ActionResult LogOn(LogOnModel model, System.Uri returnUrl)

if (ModelState.IsValid) {

            bool loggedOn = LogOn(model);

            if (loggedOn) {
                if (Url.IsLocalUrl(returnUrl.ToString())) {
                    return Redirect(returnUrl.ToString());
                }
                else {
                    return RedirectToAction("Index", "Home");
                }
            }
            else {
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(viewModel);
    }

一些风格的警察/代码分析错误迫使从字符串参数更改为 System.uri 参数,但它与标准原始参数非常相似。

只是为了澄清,在单元测试中-我想测试并断言点击Else 行的结果,它重定向到Home/Index,所以我需要将一些东西传递给(System.Uri)returnUrl,这将使它在 Url.IsLocalUrl 上返回 false 并且不抛出异常

进一步编辑:

我正在使用 MvcContrib testhelper,它非常擅长模拟很多 httpcontext 和 web 东西:

Builder = new TestControllerBuilder();
UserController = new UserController();
    Builder.InitializeController(UserController);

【问题讨论】:

    标签: asp.net-mvc-3 url local returnurl


    【解决方案1】:

    您需要在您正在单元测试的控制器上模拟 HttpContext 以及 UrlHelper 实例。如果您使用Moq,以下是该单元测试的示例:

    [TestMethod]
    public void LogOn_Should_Redirect_To_Home_If_Authentication_Succeeds_But_Not_Local_ReturnUrl_Is_Provided()
    {
        // arrange
        var sut = new AccountController();
        var model = new LogOnModel();
        var returnUrl = new Uri("http://www.google.com");
        var httpContext = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        httpContext.Setup(x => x.Request).Returns(request.Object);
        request.Setup(x => x.Url).Returns(new Uri("http://localhost:123"));
        var requestContext = new RequestContext(httpContext.Object, new RouteData());
        sut.Url = new UrlHelper(requestContext);
    
        // act
        var actual = sut.LogOn(model, returnUrl);
    
        // assert
        Assert.IsInstanceOfType(actual, typeof(RedirectToRouteResult));
        var result = (RedirectToRouteResult)actual;
        Assert.AreEqual("Home", result.RouteValues["controller"]);
        Assert.AreEqual("Index", result.RouteValues["action"]);
    }
    

    备注:由于您实际上已经展示了您正在调用以验证凭据的LogOn 实现,因此您可能需要调整单元测试以确保该方法首先在给定模型的情况下返回 true 以便输入if (loggedOn) 子句。


    更新:

    您似乎正在使用MvcContrib.TestHelper,它为您完成了所有 HttpContext 模拟设置。因此,您需要做的就是模拟单元测试的相关部分:

    [TestMethod]
    public void LogOn_Should_Redirect_To_Home_If_Authentication_Succeeds_But_Not_Local_ReturnUrl_Is_Provided()
    {
        // arrange
        var sut = new AccountController();
        new TestControllerBuilder().InitializeController(sut);
        var model = new LogOnModel();
        var returnUrl = new Uri("http://www.google.com");
        sut.HttpContext.Request.Expect(x => x.Url).Return(new Uri("http://localhost:123"));
    
        // act
        var actual = sut.LogOn(model, returnUrl);
    
        // assert
        actual
            .AssertActionRedirect()
            .ToController("Home")
            .ToAction("Index");
    }
    

    通常可以将单元测试的前 2 行移至全局 [SetUp] 方法,以避免在此控制器的每个单元测试中重复它们,这样现在您的测试就变得更清晰了:

    [TestMethod]
    public void LogOn_Should_Redirect_To_Home_If_Authentication_Succeeds_But_Not_Local_ReturnUrl_Is_Provided()
    {
        // arrange
        var model = new LogOnModel();
        var returnUrl = new Uri("http://www.google.com");
        _sut.HttpContext.Request.Expect(x => x.Url).Return(new Uri("http://localhost:123"));
    
        // act
        var actual = _sut.LogOn(model, returnUrl);
    
        // assert
        actual
            .AssertActionRedirect()
            .ToController("Home")
            .ToAction("Index");
    }
    

    【讨论】:

    • 对不起达林我还应该补充说我正在使用 MvcContrib TestHelper,请参阅上面的编辑。相当新,但我认为它涵盖了很多嘲笑问题..?
    • @Tyler,确实你应该在你的问题中提到这一点。我已经更新了我的答案,以提供一个如何使用 MvcContrib.TestHelper 实现这一目标的示例。
    • @Tyler, new Uri("google.co.uk") 是一个无效的 uri,它会抛出 UriFormatException。您需要指定协议。
    【解决方案2】:

    我登陆这里是为了寻找我的控制器中的空引用和当前答案是否有点过时。我使用最小起订量做得很好:

    var mockUrlHelper = new Mock<IUrlHelper>();
    mockUrlHelper.Setup(m => m.IsLocalUrl(It.IsAny<string>())).Returns(false);
    
    var controller = new MyController();
    controller.Url = mockUrlHelper.Object;
    

    【讨论】: