我正在尝试使用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);
}
}
}
答案 0 :(得分:0)
我不知道MVC4的所有细节,但是这个测试试图测试太多东西才能成为有效的单元测试。你有一次测试10次嘲讽的事实是明确的代码气味。尝试提取一个只验证用户标识和密码的类并测试它。路由和HTTP请求/响应测试可能更好作为集成测试 - 模拟你不拥有的东西,如HttpRequestBase是一个坏主意,因为你试图模拟难以完全理解的行为,你不能控制
这是“倾听测试”的一个很好的例子 - 如果很难测试,那么可能存在设计问题。