首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >FormsAuthenticationTicket UserData和UnitTesting

FormsAuthenticationTicket UserData和UnitTesting
EN

Stack Overflow用户
提问于 2012-07-13 20:18:38
回答 1查看 1.3K关注 0票数 0

我正在尝试在MVC中做一些单元测试,但是我的一些功能需要一个UserID才能工作(即作为外键保存到数据库中)。

UserID存储在我的FormsAuthenticationTicketUserData属性中,我如何在单元测试时‘伪造’一个UserID,这样我就可以用一个伪造的用户运行单元测试。这有可能吗?

我使用的是微软内置的单元测试系统。

我计划使用的测试代码类似于

代码语言:javascript
复制
[TestMethod]
public void EnsureAddItemAddsItems()
{
   // Arrange
   ShoppingCartController controller = new ShoppingCartController();
   // These would be populated by dummy values
   Guid itemID = Guid.Empty;
   Guid itemTypeID = Guid.Empty;

   // Act
   ViewResult result = controller.AddItem(itemTypeID, itemID);

   //Assert
   Assert.AreEqual("Your shopping cart contains 1 item(s)",result.ViewBag.Message);
}

一些示例代码可能如下所示:

代码语言:javascript
复制
public ActionResult AddItem(Guid itemType, Guid itemID, string returnAction)
{
    ShoppingCartViewModel oModel = new ShoppingCartViewModel();

    string szName = String.Empty;
    int price = 0;


    using (var context = new entityModel())
    {
        // This is the section I'm worries about
>>>>>>>>> Guid gUserID = this.GetCurrentUserID(); <<<<<<<<<

        var currentSession = this.CreateOrContinueCurrentCartSession(gUserID);

        var oItem = new ShoppingCartItem();
        oItem.Id = Guid.NewGuid();
        oItem.ItemId = itemID;
        oItem.ItemTypeId = itemType;
        oItem.ItemName = szName;
        oItem.ShoppingCartSessionId = currentSession.ID;
        oItem.Price = 1;
        context.ShoppingCartItems.AddObject(oItem);
        context.SaveChanges();
    }

    this.FlashInfo("Item added to shopping cart");
    ViewBag.Message(string.Format("Your shopping cart contains {0} item(s)",AnotherFunctionThatJustCountsTheValues()));
    return this.View();

}

高亮显示的那一行是我获取userID的地方,这个函数只是一个扩展函数,不做任何复杂的事情,它只是获取保存为FormsAuthenticationTicket.UserData字段的UserID。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-07-13 21:07:04

//这是我担心Guid gUserID = this.GetCurrentUserID();的地方

这是因为通常情况下,这一部分与控制器操作无关。所以让我们把它去掉,好吗?这样你就不再需要担心任何事情了:-)

让我们先简化你的代码,因为它包含了太多的噪声。顺便说一句,你应该考虑putting your controller action on a diet,因为它太复杂了,做的事情也太多了。

最重要的部分是:

代码语言:javascript
复制
[Authorize]
public ActionResult AddItem()
{
    Guid gUserID = this.GetCurrentUserID();
    return Content(gUserID.ToString());
}

现在,这确实很烦人,因为如果GetCurrentUserID方法驻留在控制器中,并试图读取表单身份验证cookie,那么您可能无法对其进行单独的单元测试。

如果我们的代码看起来像这样:

代码语言:javascript
复制
[MyAuthorize]
public ActionResult AddItem()
{
    var user = (MyUser)User;
    return Content(user.Id.ToString());
}

其中MyUser是自定义主体:

代码语言:javascript
复制
public class MyUser : GenericPrincipal
{
    public MyUser(IIdentity identity, string[] roles) : base(identity, roles)
    { }

    public Guid Id { get; set; }
}

那不是很棒吗?这样,控制器操作就不再需要担心cookie和票证之类的东西了。这不是它的责任。

现在让我们看看如何对其进行单元测试。我们选择我们最喜欢的模拟框架(在我的例子中是Rhino MocksMvcContrib.TestHelper)并模拟:

代码语言:javascript
复制
[TestMethod]
public void AddItem_Returns_A_Content_Result_With_The_Current_User_Id()
{
    // arrange
    var sut = new HomeController();
    var cb = new TestControllerBuilder();
    cb.InitializeController(sut);
    var user = new MyUser(new GenericIdentity("john"), null)
    {
        Id = Guid.NewGuid(),
    };
    cb.HttpContext.User = user;

    // act
    var actual = sut.AddItem();

    // assert
    actual
        .AssertResultIs<ContentResult>()
        .Content
        .Equals(user.Id.ToString());
}

所以现在剩下的就是看看定制的[MyAuthorize]属性是什么样子的了:

代码语言:javascript
复制
public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }

        var cookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie == null)
        {
            return false;
        }

        var ticket = FormsAuthentication.Decrypt(cookie.Value);
        var id = Guid.Parse(ticket.UserData);
        var identity = new GenericIdentity(ticket.Name);
        httpContext.User = new MyUser(identity, null)
        {
            Id = id
        };
        return true;
    }
}

自定义authorize属性负责读取forms身份验证cookie的UserData部分,并将当前主体设置为自定义主体。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11470362

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档