65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 端到端集成测试

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (3投票s)

2014年12月5日

CPOL

2分钟阅读

viewsIcon

36267

当控制器被视为普通类时,ASP.NET MVC 应用程序具有很强的可测试性,但这样会失去与模型验证、筛选器、方法选择器等功能的集成。

引言

本文档描述了 Xania.AspNet.Simulator 库如何帮助进行 MVC 应用程序的集成测试,使其变得令人愉快。无需再编写复杂的样板代码来测试你的控制器操作,同时提供对操作筛选器、模型验证、操作选择器等功能的支持。

背景

我一直在多个不同的 ASP.NET MVC 应用程序上工作,尽管有良好的初步意图,但这些项目中的大多数都没有考虑到可测试性。在我的最后一个项目中,我被明确要求通过重构代码来提高代码质量,简而言之,就是用服务替换 static 类,删除重复代码块,将方法拆分为更小的、职责更明确的片段等等。

我发现自己身处一种什么都不如想象中的情况。代码库远非“自文档化”,甚至 ‘Login’ 操作也远不止简单地登录用户。

解决这个问题的方法是从顶层开始,从操作层面入手,在那里我可以控制请求、响应、标头、会话、cookie、路由、筛选器、选择器、ViewData 和 ModelState,并在开始重构代码之前在该层面编写测试。不幸的是,ASP.NET MVC 框架默认情况下不支持一种简单的方法来编写支持所有这些功能的控制器操作测试。因此,我开始编写代码,将所有这些功能打包到一个易于使用的包中,名为 Xania.AspNet.Simulator

Using the Code

以以下帐户控制器实现为例

[Authorize]
public class AccountController : System.Web.Mvc.Controller
{
    public AccountController()
    {
    }

    //
    // GET: /Account/Login
    [AllowAnonymous]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View();
    }

    [HttpPost]
    [AllowAnonymous]
    public ActionResult Login(LoginViewModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            var user = // find user
            if (user != null)
            {
                return Redirect(returnUrl);
            }
            else
            {
                ModelState.AddModelError("", "Invalid username or password.");
            }
        }
         // If we got this far, something failed, redisplay form
        return View(model);
    }

    //
    // POST: /Account/LogOff
    [HttpPost]
    public ActionResult LogOff()
    {
        // sign out user
        return RedirectToAction("Index", "Home");
    }
}

让我们开始编写集成测试。

  1. 第一个 Login 操作可供匿名用户访问,并且设置了 ViewBag.ReturnUrl
    [Test]
    public void GetLoginTest() 
    {
        // arrange
        var action = new AccountController().Action(c => c.Login("[returnUrl]"));
        // act
        var result = action.Execute();
        // assert
        Assert.IsInstanceOf<ViewResult>(result.ActionResult);
        Assert.AreEqual("[returnUrl]", result.ViewBag.ReturnUrl);
    }
  2. 第二个 Login 操作可供匿名用户通过 post 方法访问,并且需要 LoginViewModel 有效。
    [Test]
    public void InvalidPostLoginTest() 
    {
        // arrange, when http method is GET
        var action = new AccountController().Action
        (c => c.Login(null), "GET" /* is default */);
    
        // assert
        Assert.IsNull(action); // not found
    }
    
    [Test]
    public void PostLoginTest() 
    {
        // arrange
        var model = new LoginViewModel 
        { UserName = "user1", Password = "passw1" };
        var action = new AccountController().Action(c => c.Login(model), "POST");
        // act
        var result = action.Execute();
        // assert, assume user exists
        Assert.IsInstanceOf<RedirectResult>(result.ActionResult);
        Assert.AreEqual("[returnUrl]", result.ViewBag.ReturnUrl);
    }
  3. logoff 操作仅可供授权用户通过 post 方法访问。
    [Test]
    public void PostLogOffTest() 
    {
        // arrange
        var action = new AccountController().Action(c => c.LogOff(), "POST");
        // act
        var result = action.Authenticate("user1", new string[0]).Execute();
        // assert
        Assert.IsInstanceOf<RedirectToRouteResult>(result.ActionResult);
    }
    
    [Test]
    public void PostLogOffUnauthenticatedTest() 
    {
        // arrange
        var action = new AccountController().Action(c => c.LogOff(), "POST");
        // act, but don't authenticate
        var result = action.Execute();
        // assert
        Assert.IsInstanceOf<HttpUnauthorizedResult>(result.ActionResult);
    }

关注点

代码库在 github 上

并且可以在 NuGet 上找到一个发行版,ID 为 Xania.AspNet.Simulator

© . All rights reserved.