需要帮助了解此代码

cho*_*bo2 2 asp.net-mvc nunit unit-testing moq

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

我正在使用Nunit和Moq作为我的框架.

问题1:

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

我有点困惑什么"??" 从来没有真正见过它.就像我甚至不知道这里发生了什么.就像他们通过界面然后"??" 标记发生并使一个新的FormsAuthenticationWraper成为?

问题2.

 public AuthenticationController(): this(null, null)
        {
        }
Run Code Online (Sandbox Code Playgroud)

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

就像它实施的是什么?这也是什么意思.除此之外,为什么不能将其排除在外?并按原样坚持默认构造函数.

问题3.

在书中(很快就是asp.net mvc 1.0),它讨论了如何实现Memembership提供程序将会有很多工作要做.所以他们使用moq模型框架让生活更轻松.

现在我的问题是他们不使用"FormsAuthentication"上的moq.他们改为创建一个界面

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


        }
Run Code Online (Sandbox Code Playgroud)

然后做一个包装

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

}
Run Code Online (Sandbox Code Playgroud)

然后最后一个属性

   public IFormsAuthentication FormsAuth
        {
            get;
            private set;
        }
Run Code Online (Sandbox Code Playgroud)

他们只有会员资格

public static MembershipProvider Provider {get; 私人集; }

我不确定是什么改变这些东西.就像我改变这条线一样?

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

我还尝试在FormsAuthentication接口和Wrapper中添加另一个方法.

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
Run Code Online (Sandbox Code Playgroud)

[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();

   }
Run Code Online (Sandbox Code Playgroud)

实际总是回到null.我尝试了ViewResult,RedirectResult和RedirectToRouteResult但是每个人都回来了.所以我不确定为什么会发生这种情况,因为我发现它很奇怪

                       FormsAuth.RedirectFromLoginPage(loginValidation.UserName, rememberMe);
Run Code Online (Sandbox Code Playgroud)

不会停止视图并开始重定向.我一开始认为一旦碰到这一行它就像一个return语句,而且没有其他代码会被执行但是htis似乎不是这样的,所以我不确定这是不是问题.

谢谢

Nol*_*rin 11

问题1

??被称为null-coalescing运算符,是C#2.0以后的一个非常有用的功能.

在你的情况下,

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
Run Code Online (Sandbox Code Playgroud)

简单地是指"分配formsAuthFormsAuth除非它为空,在这种情况下分配new FormsAuthenticationWrapper()".它基本上是一种阻止代码中空引用的方法.您还可以将其视为以下条件表达式的快捷方式:

FormsAuth = formsAuth != null ? formsAuth : new FormsAuthenticationWrapper();
Run Code Online (Sandbox Code Playgroud)

问题2

使用this(null, null)称为构造函数链接.所有这些意味着,在执行构造函数的主体之前,应该调用与两个参数相同的类(因此this,base与父类相对)的构造函数.

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

问题3

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

更新

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

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

public AuthenticationController(IFormsAuthentication formsAuth,
    MembershipProvider provider)
{
    this.FormsAuth = formsAuth;
    this.Provider = provider;
}
Run Code Online (Sandbox Code Playgroud)

在这种形式中,显而易见的是,带有两个参数的构造函数只是将类变量赋给参数的值.无参数构造函数(通常称为默认构造函数)使用默认 FormsAuthProvider对象创建一个新对象,这些对象通过构造函数链接指定.