如何使用MOQ进行模拟登录,其中AccountController具有服务构造函数参数

时间:2014-02-26 10:37:47

标签: unit-testing asp.net-mvc-4 tdd moq mstest

我正在尝试使用MOQ模拟AccountController登录方法。我有错误说要调用此方法,“Membership.Provider”属性必须是“ExtendedMembershipProvider”的实例。如果AccountController具有IWebSecurity和IOAuthWebSecurity的构造函数,则测试通过,但如果我要实现服务构造函数参数,则测试失败并显示我提到的错误。任何人都可以帮我纠正我的模拟实现吗?我是模仿,MOQ和单元测试的新手。我正在使用MS Test(创建MVC 4项目时默认的测试)。请帮忙。

工作和成功测试

AccountController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.AspNet;
using Microsoft.Web.WebPages.OAuth;
using WebMatrix.WebData;
using SampleWebApp.Filters;
using SampleWebApp.Models;

namespace SampleWebApp.Controllers
{
    [Authorize]
    [InitializeSimpleMembership]
    public class AccountController : Controller
    {
        private IWebSecurity WebSecurity { get; set; }
        private IOAuthWebSecurity OAuthWebSecurity { get; set; }

        public AccountController()
            : this(new WebSecurityWrapper(), new OAuthWebSecurityWrapper())
        {
        }

        public AccountController(IWebSecurity webSecurity, IOAuthWebSecurity oAuthWebSecurity)
        {
            WebSecurity = webSecurity;
            OAuthWebSecurity = oAuthWebSecurity;
        }

            //
    // GET: /Account/Login

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

            [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
        {
            return RedirectToLocal(returnUrl);
        }

        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", "The user name or password provided is incorrect.");
        return View(model);
    }
}
}

测试失败

AccountController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.AspNet;
using WebMatrix.WebData;
//reference namespace for services
namespace SampleWebApp.Controllers
{
    [SinglePlatformAuthorize]
    [InitializeSimpleMembership]
    public class AccountController : Controller
    {
        private IUserAccountService UserAccountService { get; set; }
        private IUserService _userService;
        private readonly IParentPortalService parentPortalService;

        public AccountController(IUserService userService, IParentPortalService parentPortalService)
        {
            this.UserAccountService = new UserAccountService();
            this._userService = userService;
            this.parentPortalService = parentPortalService;
        }

        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            Session.Clear();
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(LoginModel model, string returnUrl = "")
        {
            returnUrl = returnUrl == "" ? Url.Action("Index", "Home") : HttpUtility.UrlDecode(returnUrl);
            if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, model.RememberMe))
            {
                SessionVariables sessionVariables;

                // Set static accountid for now
                var authenticatedModel = this.UserAccountService.GetUserAuthenticatedModel(model.UserName);

               ....
                return Json(new { success = true, url = returnUrl });
            }          
        }
    }
}

单元测试代码/文件

IOAuthWebSecurity.cs

namespace SampleWebApp.Models
{
    using System.Collections.Generic;
    using DotNetOpenAuth.AspNet;
    using Microsoft.Web.WebPages.OAuth;

    public interface IOAuthWebSecurity
    {
        string GetUserName(string providerName, string providerUserId);
        bool HasLocalAccount(int userId);
        ICollection<OAuthAccount> GetAccountsFromUserName(string userName);
        bool DeleteAccount(string providerName, string providerUserId);
        AuthenticationResult VerifyAuthentication(string returnUrl);
        bool Login(string providerName, string providerUserId, bool createPersistentCookie);
        void CreateOrUpdateAccount(string providerName, string providerUserId, string userName);
        string SerializeProviderUserId(string providerName, string providerUserId);
        AuthenticationClientData GetOAuthClientData(string providerName);
        bool TryDeserializeProviderUserId(string data, out string providerName, out string providerUserId);
        ICollection<AuthenticationClientData> RegisteredClientData { get; }
        void RequestAuthentication(string provider, string returnUrl);
    }
}

IWebSecurity.cs

 namespace SampleWebApp.Models
    {
        using System.Security.Principal;

        public interface IWebSecurity
        {
            bool Login(string userName, string password, bool persistCookie = false);
            void Logout();
            string CreateUserAndAccount(string userName, string password, object propertyValues = null, bool requireConfirmationToken = false);
            int GetUserId(string userName);
            bool ChangePassword(string userName, string currentPassword, string newPassword);
            string CreateAccount(string userName, string password, bool requireConfirmationToken = false);

            IPrincipal CurrentUser { get; }
        }
    }

OAuthWebSecurityWrapper.cs

namespace SampleWebApp.Models
    {
        using System.Collections.Generic;
        using DotNetOpenAuth.AspNet;
        using Microsoft.Web.WebPages.OAuth;

        public class OAuthWebSecurityWrapper : IOAuthWebSecurity
        {
            public string GetUserName(string providerName, string providerUserId)
            {
                return OAuthWebSecurity.GetUserName(providerName, providerUserId);
            }

            public bool HasLocalAccount(int userId)
            {
                return OAuthWebSecurity.HasLocalAccount(userId);
            }

            public ICollection<OAuthAccount> GetAccountsFromUserName(string userName)
            {
                return OAuthWebSecurity.GetAccountsFromUserName(userName);
            }

            public bool DeleteAccount(string providerName, string providerUserId)
            {
                return OAuthWebSecurity.DeleteAccount(providerName, providerUserId);
            }

            public AuthenticationResult VerifyAuthentication(string returnUrl)
            {
                return OAuthWebSecurity.VerifyAuthentication(returnUrl);
            }

            public bool Login(string providerName, string providerUserId, bool createPersistentCookie)
            {
                return OAuthWebSecurity.Login(providerName, providerUserId, createPersistentCookie);
            }

            public void CreateOrUpdateAccount(string providerName, string providerUserId, string userName)
            {
                OAuthWebSecurity.CreateOrUpdateAccount(providerName, providerUserId, userName);
            }

            public string SerializeProviderUserId(string providerName, string providerUserId)
            {
                return OAuthWebSecurity.SerializeProviderUserId(providerName, providerUserId);
            }

            public AuthenticationClientData GetOAuthClientData(string providerName)
            {
                return OAuthWebSecurity.GetOAuthClientData(providerName);
            }

            public bool TryDeserializeProviderUserId(string data, out string providerName, out string providerUserId)
            {
                return OAuthWebSecurity.TryDeserializeProviderUserId(data, out providerName, out providerUserId);
            }

            public ICollection<AuthenticationClientData> RegisteredClientData { get { return OAuthWebSecurity.RegisteredClientData; } }

            public void RequestAuthentication(string provider, string returnUrl)
            {
                OAuthWebSecurity.RequestAuthentication(provider, returnUrl);
            }
        }
    }

WebSecurityWrapper.cs

 namespace SampleWebApp.Models
    {
        using System.Security.Principal;
        using System.Web;
        using WebMatrix.WebData;

        public class WebSecurityWrapper : IWebSecurity
        {
            public bool Login(string userName, string password, bool persistCookie = false)
            {
                return WebSecurity.Login(userName, password, persistCookie);
            }

            public void Logout()
            {
                WebSecurity.Logout();
            }

            public string CreateUserAndAccount(string userName, string password, object propertyValues = null, bool requireConfirmationToken = false)
            {
                return WebSecurity.CreateUserAndAccount(userName, password, propertyValues);
            }

            public int GetUserId(string userName)
            {
                return WebSecurity.GetUserId(userName);
            }

            public bool ChangePassword(string userName, string currentPassword, string newPassword)
            {
                return WebSecurity.ChangePassword(userName, currentPassword, newPassword);
            }

            public string CreateAccount(string userName, string password, bool requireConfirmationToken = false)
            {
                return WebSecurity.CreateAccount(userName, password, requireConfirmationToken);
            }

            public IPrincipal CurrentUser
            {
                get { return HttpContext.Current.User; }
            }
        }
    }

AccountControllerTests.cs

using System;
using System.Collections.Generic;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Web.WebPages.OAuth;
using Moq;
using SampleWebApp.Controllers;
using SampleWebApp.Models;

namespace SampleWebApp.UnitTests
{
    [TestClass]
    public class AccountControllerTests
    {
        private AccountController Controller { get; set; }
        private RouteCollection Routes { get; set; }
        private Mock<IWebSecurity> WebSecurity { get; set; }
        private Mock<IOAuthWebSecurity> OAuthWebSecurity { get; set; }
        private Mock<HttpResponseBase> Response { get; set; }
        private Mock<HttpRequestBase> Request { get; set; }
        private Mock<HttpContextBase> Context { get; set; }
        private Mock<ControllerContext> ControllerContext { get; set; }
        private Mock<IPrincipal> User { get; set; }
        private Mock<IIdentity> Identity { get; set; }
            //added for service constructor parameters
            private Mock<IUserAccountService> UserAccountService { get; set; }
            private Mock<IUserService> UserService { get; set; }
            private Mock<IParentPortalService> ParentPortalService { get; set; }
        public AccountControllerTests()
        {
            WebSecurity = new Mock<IWebSecurity>(MockBehavior.Strict);
            OAuthWebSecurity = new Mock<IOAuthWebSecurity>(MockBehavior.Strict);

                    //added for services constructor parameters
                    UserAccountService= new Mock<IUserAccountService>(MockBehavior.Strict);
                    UserService= new Mock<IUserService>(MockBehavior.Strict);
                    ParentPortalService= new Mock<IParentPortalService>(MockBehavior.Strict);
                    //end

            Identity = new Mock<IIdentity>(MockBehavior.Strict);
            User = new Mock<IPrincipal>(MockBehavior.Strict);
            User.SetupGet(u => u.Identity).Returns(Identity.Object);
            WebSecurity.SetupGet(w => w.CurrentUser).Returns(User.Object);

            Routes = new RouteCollection();
            RouteConfig.RegisterRoutes(Routes);

            Request = new Mock<HttpRequestBase>(MockBehavior.Strict);
            Request.SetupGet(x => x.ApplicationPath).Returns("/");
            Request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
            Request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

            Response = new Mock<HttpResponseBase>(MockBehavior.Strict);

            Context = new Mock<HttpContextBase>(MockBehavior.Strict);
            Context.SetupGet(x => x.Request).Returns(Request.Object);
            Context.SetupGet(x => x.Response).Returns(Response.Object);

            Controller = new AccountController(WebSecurity.Object, OAuthWebSecurity.Object);
                    //for service constructor parameters
                    //Controller = new AccountController(UserService.Object, ParentPortalService.Object);
            Controller.ControllerContext = new ControllerContext(Context.Object, new RouteData(), Controller);
            Controller.Url = new UrlHelper(new RequestContext(Context.Object, new RouteData()), Routes);
        }

        [TestMethod]
        public void Login_UserCanLogin()
        {
            string returnUrl = "/Home/Index";
            string userName = "user";
            string password = "password";

            WebSecurity.Setup(s => s.Login(userName, password, false)).Returns(true);
            var model = new LoginModel
            {
                UserName = userName,
                Password = password
            };

            var result = Controller.Login(model, returnUrl) as RedirectResult;
            Assert.IsNotNull(result);
            Assert.AreEqual(returnUrl, result.Url);

        }

    }
}

1 个答案:

答案 0 :(得分:0)

我不知道MVC4的所有细节,但是这个测试试图测试太多东西才能成为有效的单元测试。你有一次测试10次嘲讽的事实是明确的代码气味。尝试提取一个只验证用户标识和密码的类并测试它。路由和HTTP请求/响应测试可能更好作为集成测试 - 模拟你不拥有的东西,如HttpRequestBase是一个坏主意,因为你试图模拟难以完全理解的行为,你不能控制

这是“倾听测试”的一个很好的例子 - 如果很难测试,那么可能存在设计问题。

相关问题