如何进行ASP.NET MVC 的测试

简介:

本文参考了http://stephenwalther.com/blog/的内容。

今天需要对ASP.NET MVC的Controller进行测试,我们都知道当我们在测试工程里new一个controller时,这个controller里的httpcontext是空的,也就是session,cookie, form等都是空。

方法一:Mock controller的HttpContext, 暂时失败

那么我们如何对controller进行测试呢,我首先想到的是mock一个httpcontext,这里我用的是Rhino Mocks

public static class MvcMockHelpers

    {

        public static HttpContextBase FakeHttpContext(this MockRepository mocks)

        {

            HttpContextBase context = mocks.PartialMock<HttpContextBase>();

            HttpRequestBase request = mocks.PartialMock<HttpRequestBase>();

            HttpResponseBase response = mocks.PartialMock<HttpResponseBase>();

            HttpSessionStateBase session = mocks.PartialMock<HttpSessionStateBase>();

            HttpServerUtilityBase server = mocks.PartialMock<HttpServerUtilityBase>();

 

            SetupResult.For(context.Request).Return(request);

            SetupResult.For(context.Response).Return(response);               

            SetupResult.For(context.Session).Return(session);

            SetupResult.For(context.Server).Return(server);

 

            mocks.Replay(context);

            return context;

        }

 

        public static HttpContextBase FakeHttpContext(this MockRepository mocks, string url)

        {

            HttpContextBase context = FakeHttpContext(mocks);

            context.Request.SetupRequestUrl(url);

            return context;

        }

 

        public static void SetFakeControllerContext(this MockRepository mocks, Controller controller)

        {

            var httpContext = mocks.FakeHttpContext();

            ControllerContext context = new ControllerContext(new RequestContext(httpContext, newRouteData()), controller);

            controller.ControllerContext = context;

        }

下面我们建立一个ASP.NET MVC工程

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Mvc.Ajax;

 

namespace MVCTest.Controllers

{

    [HandleError]

    public class HomeController : Controller

    {

        public ActionResult TestSession()

        {

            Session["name"] = "Jack Wang";

            ViewData["Title"] = "Home Page";

            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            if (Session["CurrentCulture"] != null)

            {

                ViewData["CurrentCulture"] = Session["CurrentCulture"];

            }

            return View();

        }

 

        public ActionResult TestForm()

        {

            ViewData["Name"] = Request.Form["Name"];

            ViewData["Age"] = Request.Form["Age"];

            ViewData["count"] = Request.Form.Count;

            return View();

        }

 

        public ActionResult TestLogin()

        {

            if (User.Identity.IsAuthenticated)

            {

                ViewData["userName"] = User.Identity.Name;

                return View("Admin");

            }

            else

            {

                return RedirectToAction("Index");

            }

        }

 

        public ActionResult Admin()

        {

            if (User.IsInRole("Admin"))

            {

                return View("Admin");

            }

            else

            {

                return RedirectToAction("Index");

            }

        }

 

        public ViewResult Details()

        {

            ViewData["PageSize"] = Request.QueryString["PageSize"];

            ViewData["CurrentPage"] = Request.QueryString["CurrentPage"];

            ViewData["count"] = Request.QueryString.Count;

 

            return View();

        }

        public ViewResult TestCookie()

        {

            ViewData["key"] = Request.Cookies["key"].Value;

            return View();

        }

    }

}

 

测试代码

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Web.Mvc;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MVCTest;

using MVCTest.Controllers;

using MvcFakes;

using System.Web.SessionState;

using System.Web;

using System.Collections.Specialized;

using Rhino.Mocks;

namespace MVCTest.Tests.Controllers

{

    /// <summary>

    /// Summary description for HomeControllerTestByMock

    /// </summary>

    [TestClass]

    public class HomeControllerTestByMock

    {

        public HomeControllerTestByMock()

        {

            //

            // TODO: Add constructor logic here

            //

        }

        private MockRepository mocks;

        [TestInitialize]

        public void Setup()

        {

            mocks = new MockRepository();

        }

        [TestMethod]

        public void TestSession()

        {

            // Arrange

 

            HomeController controller = new HomeController();

            mocks.SetFakeControllerContext(controller);

            controller.Session.Add("name""Jack Wang");

            SetupResult.For(controller.Session["CurrentCulture"]).Return("zh-CN");

 

            mocks.ReplayAll();

            // Act

            ViewResult result = controller.TestSession() as ViewResult;

 

            // Assert

            ViewDataDictionary viewData = result.ViewData;

            Assert.AreEqual("Home Page", viewData["Title"]);

            Assert.AreEqual("Welcome to ASP.NET MVC!", viewData["Message"]);

            Assert.AreEqual(controller.Session["name"], "Jack Wang");

            Assert.AreEqual("zh-CN", viewData["CurrentCulture"]);

        }

 

    }

}

运行,测试

image

从错误信息可以看到是因为代码里Session["name"] = "Jack Wang"出错

本人排查很久,只知道mock的controllercontext的Session里没有这个Key,但现在还没有找到如何解决,哪位高人能帮吗解决?

 

二,自己写个模拟的Fake类,测试通过

 

既然这种方法不行,我们只能换一种方法,还好controller的ControllerContext可以赋值,这样我们就通过继承ControllerContext来Fake

using System;

using System.Collections;

using System.Collections.Generic;

using System.Collections.Specialized;

using System.Linq;

using System.Text;

using System.Web;

using System.Web.SessionState;

 

namespace MvcFakes

{

    public class FakeHttpSessionState : HttpSessionStateBase

    {

        private readonly SessionStateItemCollection _sessionItems;

 

        public FakeHttpSessionState(SessionStateItemCollection sessionItems)

        {

            _sessionItems = sessionItems;

        }

 

        public override void Add(string name, object value)

        {

            _sessionItems[name] = value;

        }

 

        public override int Count

        {

            get

            {

                return _sessionItems.Count;

            }

        }

 

        public override IEnumerator GetEnumerator()

        {

            return _sessionItems.GetEnumerator();

        }

 

        public override NameObjectCollectionBase.KeysCollection Keys

        {

            get

            {

                return _sessionItems.Keys;

            }

        }

 

        public override object this[string name]

        {

            get

            {

                return _sessionItems[name];

            }

            set

            {

                _sessionItems[name] = value;

            }

        }

 

        public override object this[int index]

        {

            get

            {

                return _sessionItems[index];

            }

            set

            {

                _sessionItems[index] = value;

            }

        }

 

        public override void Remove(string name)

        {

            _sessionItems.Remove(name);

        }

    }

}

 

using System;

using System.Collections.Specialized;

using System.Web;

 

namespace MvcFakes

{

 

    public class FakeHttpRequest : HttpRequestBase

    {

        private readonly NameValueCollection _formParams;

        private readonly NameValueCollection _queryStringParams;

        private readonly HttpCookieCollection _cookies;

 

        public FakeHttpRequest(NameValueCollection formParams, NameValueCollection queryStringParams,HttpCookieCollection cookies)

        {

            _formParams = formParams;

            _queryStringParams = queryStringParams;

            _cookies = cookies;

        }

 

        public override NameValueCollection Form

        {

            get

            {

                return _formParams;

            }

        }

 

        public override NameValueCollection QueryString

        {

            get

            {

                return _queryStringParams;

            }

        }

 

        public override HttpCookieCollection Cookies

        {

            get

            {

                return _cookies;

            }

        }

 

    }

}

 

using System;

using System.Security.Principal;

 

namespace MvcFakes

{

 

 

    public class FakeIdentity : IIdentity

    {

        private readonly string _name;

 

        public FakeIdentity(string userName)

        {

            _name = userName;

 

        }

 

        public string AuthenticationType

        {

            get { throw new System.NotImplementedException(); }

        }

 

        public bool IsAuthenticated

        {

            get { return !String.IsNullOrEmpty(_name); }

        }

 

        public string Name

        {

            get { return _name; }

        }

 

    }

 

 

}

 

using System;

using System.Linq;

using System.Security.Principal;

 

namespace MvcFakes

{

 

    public class FakePrincipal : IPrincipal

    {

        private readonly IIdentity _identity;

        private readonly string[] _roles;

 

        public FakePrincipal(IIdentity identity, string[] roles)

        {

            _identity = identity;

            _roles = roles;

        }

 

        public IIdentity Identity

        {

            get { return _identity; }

        }

 

        public bool IsInRole(string role)

        {

            if (_roles == null)

                return false;

            return _roles.Contains(role);

        }

    }

}

 

using System;

using System.Collections.Specialized;

using System.Security.Principal;

using System.Web;

using System.Web.SessionState;

 

namespace MvcFakes

{

    public class FakeHttpContext : HttpContextBase

    {

        private readonly FakePrincipal _principal;

        private readonly NameValueCollection _formParams;

        private readonly NameValueCollection _queryStringParams;

        private readonly HttpCookieCollection _cookies;

        private readonly SessionStateItemCollection _sessionItems;

 

        public FakeHttpContext(FakePrincipal principal, NameValueCollection formParams, NameValueCollectionqueryStringParams, HttpCookieCollection cookies, SessionStateItemCollection sessionItems )

        {

            _principal = principal;

            _formParams = formParams;

            _queryStringParams = queryStringParams;

            _cookies = cookies;

            _sessionItems = sessionItems;

        }

 

        public override HttpRequestBase Request

        {

            get

            {

                return new FakeHttpRequest(_formParams, _queryStringParams, _cookies);

            }

        }

 

        public override IPrincipal User

        {

            get

            {

                return _principal;

            }

            set

            {

                throw new System.NotImplementedException();

            }

        }

 

        public override HttpSessionStateBase Session

        {

            get

            {

                return new FakeHttpSessionState(_sessionItems);

            }

        }

 

    }

}

 

using System;

using System.Collections.Specialized;

using System.Web;

using System.Web.Mvc;

using System.Web.Routing;

using System.Web.SessionState;

 

namespace MvcFakes

{

 

    public class FakeControllerContext : ControllerContext

    {

        public FakeControllerContext(ControllerBase controller)

            : this(controller, nullnullnullnullnullnull)

        {

        }

 

        public FakeControllerContext(ControllerBase controller, HttpCookieCollection cookies)

            : this(controller, nullnullnullnull, cookies, null)

        {

        }

 

        public FakeControllerContext(ControllerBase controller, SessionStateItemCollection sessionItems)

            : this(controller, nullnullnullnullnull, sessionItems)

        {

        }

 

 

        public FakeControllerContext(ControllerBase controller, NameValueCollection formParams)

            : this(controller, nullnull, formParams, nullnullnull)

        {

        }

 

 

        public FakeControllerContext(ControllerBase controller, NameValueCollection formParams,NameValueCollection queryStringParams)

            : this(controller, nullnull, formParams, queryStringParams, nullnull)

        {

        }

 

 

 

        public FakeControllerContext(ControllerBase controller, string userName)

            : this(controller, userName, nullnullnullnullnull)

        {

        }

 

 

        public FakeControllerContext(ControllerBase controller, string userName, string[] roles)

            : this(controller, userName, roles, nullnullnullnull)

        {

        }

 

 

        public FakeControllerContext

            (

                ControllerBase controller,

                string userName,

                string[] roles,

                NameValueCollection formParams,

                NameValueCollection queryStringParams,

                HttpCookieCollection cookies,

                SessionStateItemCollection sessionItems

            )

            : base(new FakeHttpContext(new FakePrincipal(new FakeIdentity(userName), roles), formParams, queryStringParams, cookies, sessionItems),

            new RouteData(), controller)

        { }

    }

}

 

下面是测试类

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Web.Mvc;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using MVCTest;

using MVCTest.Controllers;

using MvcFakes;

using System.Web.SessionState;

using System.Web;

using System.Collections.Specialized;

namespace MVCTest.Tests.Controllers

{

    /// <summary>

    /// Summary description for HomeControllerTest

    /// </summary>

    [TestClass]

    public class HomeControllerTest

    {

        [TestMethod]

        public void TestSession()

        {

            // Arrange

            HomeController controller = new HomeController();

            var sessionItems = new SessionStateItemCollection();

            sessionItems["CurrentCulture"] = "zh-CN";

            controller.ControllerContext = new FakeControllerContext(controller, sessionItems);

            // Act

            ViewResult result = controller.TestSession() as ViewResult;

 

            // Assert

            ViewDataDictionary viewData = result.ViewData;

            Assert.AreEqual("Home Page", viewData["Title"]);

            Assert.AreEqual("Welcome to ASP.NET MVC!", viewData["Message"]);

            Assert.AreEqual(sessionItems["name"], "Jack Wang");

            Assert.AreEqual("zh-CN", viewData["CurrentCulture"]);

        }

        [TestMethod]

        public void TestFakeFormParams()

        {

 

            var controller = new HomeController();

 

            var formParams = new NameValueCollection { { "Name""Jack" }, { "Age""28" } };

            controller.ControllerContext = new FakeControllerContext(controller, formParams);

 

            var result = controller.TestForm() as ViewResult;

            Assert.AreEqual("Jack", result.ViewData["Name"]);

            Assert.AreEqual("28", result.ViewData["Age"]);

            Assert.AreEqual(formParams.Count, result.ViewData["count"]);

        }

 

        [TestMethod]

        public void TestFakeQueryStringParams()

        {

            var controller = new HomeController();

 

            var queryStringParams = new NameValueCollection { { "PageSize""10" }, { "CurrentPage""5"} };

            controller.ControllerContext = new FakeControllerContext(controller, null, queryStringParams);

 

            var result = controller.Details() as ViewResult;

            Assert.AreEqual("10", result.ViewData["PageSize"]);

            Assert.AreEqual("5", result.ViewData["CurrentPage"]);

            Assert.AreEqual(queryStringParams.Count, result.ViewData["count"]);

        }

 

        [TestMethod]

        public void TestFakeUser()

        {

            var controller = new HomeController();

 

            controller.ControllerContext = new FakeControllerContext(controller, "Jack Wang");

            var result = controller.TestLogin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(ViewResult));

            ViewDataDictionary viewData = ((ViewResult)result).ViewData;

            Assert.AreEqual("Jack Wang", viewData["userName"]);

 

 

            controller.ControllerContext = new FakeControllerContext(controller);

            result = controller.TestLogin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

        }

 

 

        [TestMethod]

        public void TestFakeUserRoles()

        {

 

            var controller = new HomeController();

 

 

            controller.ControllerContext = new FakeControllerContext(controller, "Jack Wang"newstring[] { "Admin" });

            var result = controller.Admin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(ViewResult));

 

 

            controller.ControllerContext = new FakeControllerContext(controller);

            result = controller.Admin() as ActionResult;

            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));

        }

 

 

 

        [TestMethod]

        public void TestCookies()

        {

 

            var controller = new HomeController();

 

 

            var cookies = new HttpCookieCollection();

            cookies.Add(new HttpCookie("key""a"));

            controller.ControllerContext = new FakeControllerContext(controller, cookies);

            var result = controller.TestCookie() as ViewResult;

 

 

            Assert.AreEqual("a", result.ViewData["key"]);

        }

    }

}

测试完全通过

image 

 

总结: 个人感觉ASP.NET MVC的测试是很不友好的,和ruby on rails相比,在测试这一块还有很大的距离,希望正式版里这一块能够加强

 

王德水 写于2008-01-05 00:30分

 

本文源码下载

本文转自敏捷的水博客园博客,原文链接http://www.cnblogs.com/cnblogsfans/archive/2009/01/05/1368630.html如需转载请自行联系原作者


王德水

相关文章
|
5天前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
16 7
|
14天前
|
jenkins 测试技术 持续交付
解锁.NET项目高效秘籍:从理论迷雾到实践巅峰,持续集成与自动化测试如何悄然改变游戏规则?
【8月更文挑战第28天】在软件开发领域,持续集成(CI)与自动化测试已成为提升效率和质量的关键工具。尤其在.NET项目中,二者的结合能显著提高开发速度并保证软件稳定性。本文将从理论到实践,详细介绍CI与自动化测试的重要性,并以ASP.NET Core Web API项目为例,演示如何使用Jenkins和NUnit实现自动化构建与测试。每次代码提交后,Jenkins自动触发构建流程,通过编译和运行NUnit测试确保代码质量。这种方式不仅节省了时间,还能快速发现并解决问题,推动.NET项目开发迈向更高水平。
32 8
|
15天前
|
测试技术 API 开发者
.NET单元测试框架大比拼:MSTest、xUnit与NUnit的实战较量与选择指南
【8月更文挑战第28天】单元测试是软件开发中不可或缺的一环,它能够确保代码的质量和稳定性。在.NET生态系统中,MSTest、xUnit和NUnit是最为流行的单元测试框架。本文将对这三种测试框架进行全面解析,并通过示例代码展示它们的基本用法和特点。
29 7
|
3天前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
12 0
|
11天前
|
Java Spring UED
Spring框架的异常处理秘籍:打造不败之身的应用!
【8月更文挑战第31天】在软件开发中,异常处理对应用的稳定性和健壮性至关重要。Spring框架提供了一套完善的异常处理机制,包括使用`@ExceptionHandler`注解和配置`@ControllerAdvice`。本文将详细介绍这两种方式,并通过示例代码展示其具体应用。`@ExceptionHandler`可用于控制器类中的方法,处理特定异常;而`@ControllerAdvice`则允许定义全局异常处理器,捕获多个控制器中的异常。
29 0
|
11天前
|
API 开发者 Java
API 版本控制不再难!Spring 框架带你玩转多样化的版本管理策略,轻松应对升级挑战!
【8月更文挑战第31天】在开发RESTful服务时,为解决向后兼容性问题,常需进行API版本控制。本文以Spring框架为例,探讨四种版本控制策略:URL版本控制、请求头版本控制、查询参数版本控制及媒体类型版本控制,并提供示例代码。此外,还介绍了通过自定义注解与过滤器实现更灵活的版本控制方案,帮助开发者根据项目需求选择最适合的方法,确保API演化的管理和客户端使用的稳定与兼容。
44 0
|
2月前
|
测试技术 C#
.NET单元测试使用Bogus或AutoFixture按需填充的几种方式和最佳实践
【7月更文挑战第13天】AutoFixture 和 Bogus 都是流行的 C#库,用于在单元测试中按需填充测试数据。以下是它们的几种使用方式和最佳实践:一、AutoFixture:1.直接定制 2.使用匿名函数 3.实现ICustomization接口 4.使用Build方法。 二、最佳实践Bogus:1.安装2.使用。
|
27天前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
29 0
|
27天前
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
|
27天前
分享一份 .NET Core 简单的自带日志系统配置,平时做一些测试或个人代码研究,用它就可以了
分享一份 .NET Core 简单的自带日志系统配置,平时做一些测试或个人代码研究,用它就可以了