ASP.NET MVC原理实战(一):为路由编写单元测试

路由简介

做过ASP.NET MVC开发的人都知道,一个页面的响应是通过前台url请求后端的controller在执行对应的action,然后返回视图。比如要访问"www.website.com/home/index",默认请求的是HomeController下的Index()方法。

public class HomeController : Controller
{
    public ActionResult Index()
    {
    
        //logic
        
        return View();
    }
}

所以验证路由是否与url匹配的基本思路就是:

  • 输入:~/home/index
  • 输出: controller=home,action=index

准备工作

在编写单元测试之前,先简单介绍几个重要的类型。

HttpContextBase

在路由解析得时候需要HttpContextBase对象,HttpContextBase中包含了两个重要的类型HttpRequestBaseHttpResponseBase,其中HttpRequestBase里面包含了HTTP请求信息。在这次的单元测试中,只需要它的两个只读(Read Only)成员。

public abstract class HttpRequestBase
{
    // ... 省略其他成员
    
    // URL中的相对路径,如"www.website.com/home/index"会被转化为"~/home/index"
    public virtual string AppRelativeCurrentExecutionFilePath { get; }
    
    // HTTP请求方法
    public virtual string HttpMethod { get; }
}

HttpResponseBase中包含了HTTP响应信息,因为我们只需要拿到controller和action中的值即可,不需要任何响应信息,所以在此不作讨论。

RouteCollection

它是ASP.NET路由的路由集合,所有注册过的路由对象都会放在该集合中。打开项目中App_Start文件夹下的RouteConfig.cs文件,会看到RegisterRoutes这个方法的routes参数就是RouteCollection类型。我们一般使用该类型的扩展方法MapRoute来注册路由。

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
}

RouteCollection中有一个GetRouteData方法来获取路由对象。路由通过这个方法来获取与请求中的url相匹配的RouteData对象。如果没有匹配到,这个方法就会返回null,这个方法需要一个HttpContextBase类型的参数,在编写测试时,我们需要创建一个HttpContextBase对象来模拟请求。

public class RouteCollection
{
    // ...省略其他成员
    public RouteData GetRouteData(HttpContextBase httpContext);
}

RouteData

顾名思义,RouteData存储了当前路由的所有信息。

public class RouteData
{
    // ...省略其他成员
    
    public RouteValueDictionary Values { get; }
}

我们看到Values属性它是RouteValueDictionary类型的,这个类型其实就是字典类型,它实现了IDictionary接口。controller、action的 值就以Key-Value的形式保存在Values

编写单元测试

创建项目

  1. 打开宇宙第一IDE——Visual Studi,新建一个ASP.NET MVC项目

  2. 添加MVC核心文件和引用,以及勾选单元测试。

  3. 删掉单元测试中的默认文件"UnitTest1.cs",重新添加一个类文件名字“RouteMatchTest”。

    内容如下:

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace RouteTestDemo.Tests
    {
    [TestClass]
    public class RouteMatchTest
    {
    
    }
    }
  4. 通过Nuget管理器为单元测试项目引入Moq。

  5. RouteMatchTest类中,添加HttpContextSimulation方法模拟请求。

    public HttpContextBase HttpContextSimulation(string requestUrl,string httpMethod)
    { 
        // 创建HttpContextBase对象
      Mock<HttpContextBase> mockHttpContext = new Mock<HttpContextBase>();
    
        // 创建HttpRequestBase对象
      Mock<HttpRequestBase> mockRequest = new Mock<HttpRequestBase>();
      mockRequest.Setup(m => m.AppRelativeCurrentExecutionFilePath).Returns(requestUrl);
      mockRequest.Setup(m => m.HttpMethod).Returns(httpMethod);
    
        // 创建HttpResponseBase对象
      Mock<HttpResponseBase> mockResponse = new Mock<HttpResponseBase>();
    
        // 将前面创建的两个对象,分别赋值给HttpContextBase对象的Request和Respone属性成员
      mockHttpContext.Setup(m => m.Request).Returns(mockRequest.Object);
      mockHttpContext.Setup(m => m.Response).Returns(mockResponse.Object);
    
        // 返回HttpResponseBase对象
      return mockHttpContext.Object;
    }
  6. 然后我们来编写验证url是否和路由匹配的方法。

    public void Can_Url_Match_Route(string url,string controller,string action)
    {
      // Arrange
          // 创建匿名方法:比较两个字符串的值是否相等
      Func<string, string, bool> comparer = (v1, v2) =>
      {
          return StringComparer.CurrentCultureIgnoreCase.Compare(v1, v2) == 0;
      };
    
      // 注册路由表
      RouteCollection routes = new RouteCollection();
      RouteConfig.RegisterRoutes(routes);
    
      // Action
          // 获取匹配到的route对象
      RouteData result = routes.GetRouteData(httpContext);
    
      // Assert
          // 将Values中的controller、action的值和期望值进行对比
      Assert.IsTrue(comparer(result.Values["controller"].ToString(), controller));
      Assert.IsTrue(comparer(result.Values["action"].ToString(), action));
    }
  7. 添加以下方法,点击“测试资源管理器中”的运行,测试通过。

    [TestMethod]
    public void Default_Route_Match_Test()
    {
     Can_Url_Match_Route("~/home/index", "home", "index");
    }

    修改一下上面的方法

  8. 修改上面的方法

    [TestMethod]
    public void Default_Route_Match_Test()
    {
     Can_Url_Match_Route("~/home/index/index/index", "home", "index");
    }

    再次运行单元测试,很明显通过不了,所以我们就可以用这种方法来测试我们所编写的url能否匹配到正确的controller和action。

猜你喜欢

转载自www.cnblogs.com/xscape/p/10299984.html