【问题标题】:Unit-test simple-membership provider?单元测试简单会员提供者?
【发布时间】:2013-09-21 07:14:23
【问题描述】:

如何对使用 Simple Membership 提供程序的控制器进行单元测试?

Controller 将 MyViewModel 对象作为输入并将其插入 DB。操作成功完成后,用户将被重定向到仪表板。

Controller 将 WebSecurity 作为依赖项。因此,当单元测试时,我在以下行中得到 HttpContext 的参数空异常

userLakshya.UserId = WebSecurity.HasUserId ? WebSecurity.CurrentUserId : -1;

如何将 HttpContext 参数传递给控制器​​?

代码清单:

    [HttpPost]
    public ActionResult Create(MyViewModel myVM)
    {
        MyModel myModel = myVM.Model;
        if (ModelState.IsValid)
        {
            userLakshya.UserId = WebSecurity.HasUserId ? WebSecurity.CurrentUserId : -1;
            db.MyModels.Add(myModel);
            db.SaveChanges();
            return RedirectToAction("Dashboard");
        }
        return View(myVM);
    }

    [TestMethod]
    public void TestLoginAndRedirectionToDashboard()
    {
        MyController cntlr = new MyController();
        var ret = ulCntlr.Create(new MyViewModel(){
            //Initialize few properties to test
        });
        /*
         * Controller throws parameter null exception for HttpContext
         * => Issue: Should I pass this? since the controller uses WebSecurity inside
         * */
        ViewResult vResult = ret as ViewResult;
        if (vResult != null)
        {
            Assert.IsInstanceOfType(vResult.Model, typeof(UserLakshya));
            UserLakshya model = vResult.Model as UserLakshya;

            if (model != null)
            {
                //Assert few properties from the return type.
            }
        }
    }  

【问题讨论】:

    标签: c# asp.net-mvc unit-testing asp.net-mvc-4 tdd


    【解决方案1】:

    您遇到的问题是您的 Create 方法违反了Dependency Inversion Principle,即您的方法应该“依赖于抽象。不要依赖于混凝土”。

    你应该重构你的代码,而不是直接使用 WebSecurity 类,而是使用抽象,例如您可以创建一个 ISecurity 接口。您的具体版本的 ISecurity(例如 Security)可以包含对 WebSecurity 的引用,从而消除直接依赖关系。

    (您已经为您的数据库依赖项(即您的 db 成员)执行此操作,您只需要针对代码的安全方面执行此操作。)

    例如

    public Interface ISecurity
    {
     int GetUserId();
    }
    

    然后代替:

    userLakshya.UserId = WebSecurity.HasUserId ? WebSecurity.CurrentUserId : -1;
    

    你可以使用:

    userLakshya.UserId = security.GetUserId();
    

    然后要测试“创建”控制器,您可以使用模拟框架(例如 Moq)模拟 ISecurity 的行为(实际上我还建议模拟您的“db”对象的行为)。使用 Moq 代码模拟的示例是:

    var mock = new Mock<ISecurity>();
    mock.Setup(security=> security.GetUserId()).Returns("ATestUser");
    

    【讨论】:

    • 1. WebSecurity 没有实现任何接口(它是静态类) 2. 在我的代码中,Create 是操作方法,因此从客户端调用。传递WebSecurityISecurity等附加参数是否可行
    • 假设我使用默认构造函数来实例化一个自定义类,以保存WebSecurity.HasUserIdWebSecurity.CurrentUserId。我在操作上应用Authorize 过滤器属性。我认为构造函数会在Authorize 之前首先被调用。在这种情况下,WebSecurity 将为空。我说的对吗?
    • @MikeCole 是的 Moq 使用 Mock 来实例化模拟对象。请参阅第二个示例here。当他们将框架命名为“Moq”时,我也认为他们会改用“Moq”。
    猜你喜欢
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 2017-08-23
    • 2017-02-02
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多