2

我正在尝试学习单元测试。我正在尝试对我在 asp.net mvc 1.0 中制作的一些 Memembership 内容进行单元测试。我一直在关注一本关于 MVC 的书,我对一些希望有人可以为我澄清的东西感到困惑。

我在我的框架中使用 Nunit 和 Moq。

问题一:

  public AuthenticationController(IFormsAuthentication formsAuth, MembershipProvider provider)
        {
            FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
            Provider = provider ?? Membership.Provider;
        }

我有点困惑什么“??” 我以前从未真正见过它。就像我什至不知道这里到底发生了什么。就像他们传递界面然后“??” 标记发生并制作了一个新的 FormsAuthenticationWraper?

问题2。

 public AuthenticationController(): this(null, null)
        {
        }

我知道这是默认构造函数,但我不确定为什么 ": this(null,null)" 这样做。

比如它在执行什么?这也指的是什么。最重要的是,为什么不能把它排除在外?并且保持默认构造函数不变。

问题 3。

在这本书(asp.net mvc 1.0 快速)中,它谈到了如何实现 Memembership 提供程序将是很多工作。所以他们使用最小起订量模型框架让生活更轻松。

现在我的问题是他们不使用“FormsAuthentication”上的起订量。他们改为制作一个界面

   public interface IFormsAuthentication
        {
            void SetAuthCookie(string userName, bool createPersistentCookie);
            void SignOut();


        }

然后做一个包装

公共类 FormsAuthenticationWrapper : IFormsAuthentication { public void SetAuthCookie(string userName, bool createPersistentCookie) { FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); } public void SignOut() { FormsAuthentication.SignOut(); }

}

然后最后一个属性

   public IFormsAuthentication FormsAuth
        {
            get;
            private set;
        }

与会员资格一样,他们只有

公共静态 MembershipProvider 提供者 { 获取;私人套装;}

我也不确定要改变什么。就像我也会改变这条线?

FormsAuth = formsAuth ?? 新的 FormsAuthenticationWrapper();

我还尝试将另一种方法添加到 FormsAuthentication 接口和包装器中。

public void RedirectFromLoginPage(string userName, bool createPersistentCookie) { FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie); }

然而我不确定发生了什么,但我的单元测试总是失败,不管我试图做什么来修复它。

     public ActionResult Login(string returnUrl, FormCollection form, bool rememberMe)
            {
                LoginValidation loginValidation = new LoginValidation();
                try
                {
                    UpdateModel(loginValidation, form.ToValueProvider());

                }
                catch
                {

                    return View("Login");
                }

                if (ModelState.IsValid == true)
                {

                    bool valid = authenticate.VerifyUser(loginValidation.UserName, loginValidation.Password);

                    if (valid == false)
                    {
                        ModelState.AddModelError("frm_Login", "Either the Password or UserName is invalid");

                    }
                    else if (string.IsNullOrEmpty(returnUrl) == false)
                    {
                        /* if the user has been sent away from a page that requires them to login and they do 
                         * login then redirect them back to this area*/
                        return Redirect(returnUrl);
                    }
                    else
                    {

                       FormsAuth.RedirectFromLoginPage(loginValidation.UserName, rememberMe);
                    }

                }


                return View("Login");


Here is my test

[测试] public void Test_If_User_Is_Redirected_Back_To_Page_They_Came_From_After_Login() { System.Diagnostics.Debugger.Break();

       var formsAuthenticationMock =  new Mock<AuthenticationController.IFormsAuthentication>();

       var membershipMock = new Mock<MembershipProvider>();

       membershipMock.Setup(m => m.ValidateUser("chobo2", "1234567")).Returns(true);


       // Setup controller
       AuthenticationController target = new AuthenticationController(formsAuthenticationMock.Object, membershipMock.Object);


       // Execute
       FormCollection form = new FormCollection();
       form.Add("Username", "chobo2");
       form.Add("password", "1234567");

       ViewResult actual = target.Login(null, form, false) as ViewResult;

       Assert.That(actual.View, Is.EqualTo("home"));
       formsAuthenticationMock.Verify();

   }

实际总是返回为空。我尝试了 ViewResult、RedirectResult 和 RedirectToRouteResult,但每个人都返回 null。所以我不确定为什么会发生这种情况,因为我首先觉得很奇怪

                       FormsAuth.RedirectFromLoginPage(loginValidation.UserName, rememberMe);

不停止视图并开始重定向。起初我以为一旦它到达这一行,它就像一个返回语句,那就是它不会执行其他代码,但 htis 似乎不是这种情况,所以我不确定这是否是问题所在。

谢谢

4

7 回答 7

11

问题 1

??被称为null-coalescing operator,是 C# 2.0 及以后的一个非常有用的特性。

在你的情况下,

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

仅表示“分配给formsAuthFormsAuth除非它为空,在这种情况下分配new FormsAuthenticationWrapper()”。它基本上是一种防止代码中出现空引用的方法。您也可以将其视为以下条件表达式的快捷方式:

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();

问题2

的使用this(null, null)称为构造函数链接。所有这一切意味着同一个类中的构造函数(因此thisbase与父类相反)接受两个参数,应该在构造函数的主体执行之前调用。

重载构造函数是一种常见做法,可以让开发人员在只想使用默认属性/设置时更轻松地创建新对象。

问题 3

正如其他人所提到的,这确实属于一个单独的问题。与前两个不同,它更具体到上下文/您的代码,而不是 C# 的语言特性。

更新

好的,我现在所做的实际上是重写了这里的两个构造函数,因为我认为将它们放在另一种(实际上等效的)形式中可能会更清晰一些,并且可能也是更好的设计实践。此处不需要空值合并运算符。

public AuthenticationController()
    : this(new FormsAuthenticationWrapper(), Membership.Provider)
{
}

public AuthenticationController(IFormsAuthentication formsAuth,
    MembershipProvider provider)
{
    this.FormsAuth = formsAuth;
    this.Provider = provider;
}

在这种形式中,很明显,接受两个参数的构造函数只是将类变量分配给参数的值。无参数构造函数(通常称为默认构造函数)只是使用默认值 FormsAuth和对象创建一个新对象Provider,这些对象是通过构造函数链接指定的。

于 2009-06-22T12:58:29.993 回答
1

问题一:?? 是空合并运算符。这 ??运算符检查表达式左侧提供的值是否为空,如果是,则返回表达式右侧指示的替代值。

在您的情况下,它会检查是否formsAuth为空,如果为空,则返回一个新的 FormsAuthenticationWrapper()。

于 2009-06-22T12:57:06.627 回答
1

这 ??操作员说“使用这个,除非它是空的,在这种情况下使用这个其他的东西”。

所以,这行代码:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

是相同的:

if ( formsAuth != null ) FormsAuth = formsAuth
else FormsAuth = new FormsAuthenticationWrapper();
于 2009-06-22T12:57:55.783 回答
0

回答 Q2

它正在重载构造函数。

如果意味着调用

Foo() 

和调用一样

Foo(null, null)
于 2009-06-22T13:00:10.693 回答
0

问题一:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
Provider = provider ?? Membership.Provider;

等于:

FormsAuth = (formsAuth == null ? new FormsAuthenticationWrapper() : formsAuth);
Provider = (provider == null ? Membership.Provider : provider);

问题2:

它只是将 null 传递给 formsAuth 和提供者构造函数参数。恕我直言,这不是一个好习惯。另一个没有参数的构造函数会更合适。

编辑:这没有任何意义。对不起,我很着急,并没有真正意识到这是一个构造函数调用另一个构造函数。

我现在没有时间回答问题 3,我稍后再回答……

于 2009-06-22T13:01:36.277 回答
0

问题 1: 操作??员只是说“如果它不为空,则取我左边的任何东西 - 如果是,取我右边的任何东西”。所以你的代码:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

相当于

if (formsAuth != null) {
    FormsAuth = formsAuth;
} else {
    FormsAuth 0 new FormsAuthenticationWrapper();
}

问题2::this(null, null)语法是“构造函数继承”的简写(我的命名......)。你的代码

public AuthenticationController(): this(null, null)
    {
    }
public AuthenticationController(IFormsAuthentication formsAuth, MembershipProvider  provider)
    {
        FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
        Provider = provider ?? Membership.Provider;
    }

相当于

public AuthenticationController()
    {
        FormsAuth = new FormsAuthenticationWrapper();
        Provider = Membership.Provider;
    }
public AuthenticationController(IFormsAuthentication formsAuth, MembershipProvider provider)
    {
        FormsAuth = formsAuth;
        Provider = provider;
    }
于 2009-06-22T13:01:58.130 回答
0

问题2

public AuthenticationController(): this(null, null)
{
}

AuthenticationController 的无参数构造函数将调用接受 IFormsAuthentication 和 MembershipProvider 的构造函数,传递两个空值(这是在执行无参数构造函数代码块中的任何代码之前完成的)。由于两个参数构造函数使用 null-coalescing (??) 运算符来分配变量并且传递的参数为 null,因此新的 MembershipProvider 与 Membership.Provider 对象一起使用。

如果没有显式定义此构造函数,则将使用默认的无参数构造函数。如果创建了新的 AuthenticationController(没有将任何参数传递给构造函数),这可能会导致意外行为,因为成员变量不会被初始化。

于 2009-06-22T13:19:47.987 回答