• 【MVC5】First Unit Test


    1.控制器测试

    注意点:

        1.控制器中不要包含业务逻辑

        2.通过构造函数传递服务依赖

    例:MathController中有一个Add的Action

    using FirstUnitTest.Services;
    using System.Web.Mvc;
    
    namespace FirstUnitTest.Controllers
    {
        public class MathController : Controller
        {
            IMathService _service;
    
            public MathController(IMathService service) {
                _service = service;
            }
    
            // GET: Math
            public RedirectToRouteResult Index()
            {
                return RedirectToAction("Add");
                //return View();
            }
    
            [HttpGet]
            public ActionResult Add()
            {
                return View();
            }
    
            [HttpPost]
            public ViewResult Add(int left, int right)
            {
                ViewBag.Result = _service.Add(left, right);
                return View();
            }
        }
    }

    IMathService定义如下,很显然是个求和方法:

    namespace FirstUnitTest.Services
    {
        public interface IMathService
        {
            int Add(int left, int right);
        }
    }

    编写一个假Service,给Controller提供一个假的业务逻辑层。

    using FirstUnitTest.Services;
    
    namespace FirstUnitTest.Tests.Services
    {
        public class SpyMathService : IMathService
        {
            public int Add_Left;
            public int Add_Right;
            public int Add_Result;
            
            public int Add(int left, int right)
            {
                Add_Left = left;
                Add_Right = right;
    
                return Add_Result;
            }
        }
    }

    注意:上面的SpyService中没有对两个值求和,因为我们只关注MathService的Input和Output(Input就是left和right参数,Output就是返回值)。

    测试方法:

    [TestMethod]
    public void AddUseMathService()
    {
        SpyMathService service = new SpyMathService() { Add_Result = 42 };
        MathController controller = new MathController(service);
        ViewResult result = controller.Add(4, 12);
    
        Assert.AreEqual(service.Add_Result, result.ViewBag.Result);
        Assert.AreEqual(4, service.Add_Left);
        Assert.AreEqual(12, service.Add_Right);
    }

    Redirect测试(上面MathController中的Index Action):

    [TestMethod]
    public void RedirectToAdd()
    {
        SpyMathService service = new SpyMathService();
        MathController controller = new MathController(service);
    
        RedirectToRouteResult result = controller.Index();
    
        Assert.AreEqual("Add", result.RouteValues["action"]);
    }

    2.路由测试

    默认MVC项目的路由如下:

    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace FirstUnitTest
    {
        public class RouteConfig
        {
            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 }
                );
            }
        }
    }

    测试方法:

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace FirstUnitTest.Tests.Routes
    {
        [TestClass]
        public class RouteTest
        {
            /// <summary>
            /// 测试IngoreRoute函数调用
            /// </summary>
            [TestMethod]
            public void RouteForEmbeddedResource()
            {
                // Arrange
                var mockContext = new Mock<HttpContextBase>();
                mockContext.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath)
                    .Returns("~/handler.axd");
                var routes = new RouteCollection();
                //MvcApplication.RegisterRoutes(routes);
                RouteConfig.RegisterRoutes(routes);
    
                // Act
                RouteData routeData = routes.GetRouteData(mockContext.Object);
    
                // Assert
                Assert.IsNotNull(routeData);
                Assert.IsInstanceOfType(routeData.RouteHandler, typeof(StopRoutingHandler));
            }
    
            /// <summary>
            /// 测试MapRoute函数调用
            /// </summary>
            [TestMethod]
            public void RouteToHomePae()
            {
                // Arrange
                var mockContext = new Mock<HttpContextBase>();
                mockContext.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath)
                    .Returns("~/");
                var routes = new RouteCollection();
                RouteConfig.RegisterRoutes(routes);
    
                // Act
                RouteData routeData = routes.GetRouteData(mockContext.Object);
    
                // Assert
                Assert.IsNotNull(routeData);
                Assert.AreEqual("Home", routeData.Values["controller"]);
                Assert.AreEqual("Index", routeData.Values["action"]);
                Assert.AreEqual(UrlParameter.Optional, routeData.Values["id"]);
            }
    
            // 不需要对不匹配路由编写测试代码
        }
    }

    使用Mock需要安装Moq包(需要设定ProjectName参数,否则会默认安装到Web工程)

    Install-Package moq -ProjectName FirstUnitTest.Tests

    3.验证测试

    Movie模型:

    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity;
    
    namespace FirstUnitTest.Models
    {
        public class Movie
        {
            public int Id { get; set; }
            [Required]
            public string Title { get; set; }
            [Required]
            [Range(1920, 2015)]
            public int ReleaseYear { get; set; }
            public int RunTime { get; set; }
        }
    
        public class MovieDb : DbContext
        {
            public DbSet<Movie> Movies { get; set; }
        }
    }

    属性验证:

    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using FirstUnitTest.Models;
    using System.ComponentModel.DataAnnotations;
    
    namespace FirstUnitTest.Tests.Validation
    {
        [TestClass]
        public class MovieValidationTest
        {
            [TestMethod]
            public void TitleRequireTest()
            {
                System.Threading.Thread.CurrentThread.CurrentUICulture 
                    = new System.Globalization.CultureInfo("zh-cn");
                Movie movie = new Movie();
                ValidationContext context = new ValidationContext(movie, null, null) {
                    DisplayName = "Title",
                    MemberName = "Title",
                };
                RequiredAttribute validator = new RequiredAttribute();
    
                try
                {
                    validator.Validate(movie.Title, context);
                }
                catch (ValidationException ex)
                {
                    // 错误消息的语言由当前线程的CurrentUICulture决定
                    Assert.AreEqual("Title 字段是必需的。", ex.Message);
                    //throw;
                }
            }
        }
    }

    ※本文参照《ASP.NET MVC 5高级编程(第5版)》

  • 相关阅读:
    02安卓用户界面优化之(三)如何使用菜单
    07-业务敏捷:帮助DevOps快速落地的源动力
    转型之路:企业实施DevOps的常见路径和问题
    价值流分析:关于DevOps转型,我们应该从何处入手
    DevOps的衡量:你是否找到了DevOps的实施路线图
    DevOps的实施:到底是工具先行还是文化先行
    DevOps的价值:数字化转型时代,DevOps是必选项
    DevOps的“定义”:DevOps究竟要解决什么问题
    Jenkins产品经理是如何设计产品的
    关于DevOps组织和文化的那些趣事儿.
  • 原文地址:https://www.cnblogs.com/Ryukaka/p/mvc5_first_unit_test.html
Copyright © 2020-2023  润新知