Нужна помощь в понимании этого кода

Я пытаюсь изучить модульное тестирование. Я пытаюсь выполнить модульное тестирование некоторых материалов о членстве, которые я делаю в asp.net mvc 1.0. Я слежу за книгой по MVC, и меня смущают некоторые вещи, которые, надеюсь, кто-то может прояснить для меня.

Я использую Nunit и Moq для своих фреймворков.

Вопрос 1:

  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 потребует немало работы. Поэтому они используют структуру мокапов moq, чтобы облегчить жизнь.

Теперь мой вопрос в том, что они не используют moq для «FormsAuthentication». Вместо этого они создают интерфейс

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


        }

Затем сделайте обертку

открытый класс FormsAuthenticationWrapper: IFormsAuthentication {общедоступный void SetAuthCookie (строка 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 (строка 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, но все возвращаются с нулевым значением. Поэтому я не уверен, почему это происходит, потому что сначала мне показалось странным, что

                       FormsAuth.RedirectFromLoginPage(loginValidation.UserName, rememberMe);

Не останавливает просмотр и начинает перенаправлять. Сначала я подумал, что как только он попадает в эту строку, это похоже на оператор return, и что другой код не будет выполняться, но похоже, что это не так, поэтому я не уверен, может ли это быть проблемой.

Спасибо


person chobo2    schedule 22.06.2009    source источник
comment
Вам, вероятно, следует разделить свои вопросы на отдельные посты. Вопросы 1 и 2 связаны между собой и могут быть одним постом, но вопрос 3, скорее всего, должен быть сам по себе.   -  person Tomas Aschan    schedule 22.06.2009
comment
вопрос 3 на самом деле больше, чем один вопрос   -  person Colin Pickard    schedule 22.06.2009


Ответы (7)


Вопрос 1

?? называется оператором объединения с нулем , и это очень полезная функция начиная с C # 2.0.

В твоем случае,

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

просто означает «присвоить formsAuth FormsAuth, если он не равен нулю, и в этом случае назначить new FormsAuthenticationWrapper()». По сути, это способ предотвратить пустые ссылки в вашем коде. Вы также можете думать об этом как о сокращении для следующего условного выражения:

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

вопрос 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;
}

В этой форме должно быть очевидно, что конструктор, который принимает два параметра, просто присваивает переменным класса значения аргументов. Конструктор без параметров (часто называемый конструктором по умолчанию) просто создает новый объект, используя объекты default FormsAuth и Provider, которые указываются с помощью цепочки конструкторов .

person Noldorin    schedule 22.06.2009
comment
Хм, хорошо, это становится более понятным. С конструктором по умолчанию он наследует материал от контроллера, верно? (Хотя я не уверен, что именно наследуется). Я не знаю, смогу ли я когда-нибудь сделать это через миллион лет, смеется. Мне все еще интересно, когда может быть, что formsAuth будет нулевым? Я этого не вижу. Я также отправлю новый вопрос к вопросу 3 - person chobo2; 24.06.2009

Вопрос 1: ?? это нулевой оператор объединения. ?? Оператор проверяет, является ли значение, указанное в левой части выражения, нулевым, и если да, он возвращает альтернативное значение, указанное в правой части выражения.

В вашей ситуации он проверяет, является ли formsAuth нулевым, и возвращает новый FormsAuthenticationWrapper (), если он равен нулю.

person Colin Pickard    schedule 22.06.2009

?? оператор говорит: «используйте это, если он не равен нулю, в этом случае используйте это другое».

Итак, эта строка кода:

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();

Такой же как:

if ( formsAuth != null ) FormsAuth = formsAuth
else FormsAuth = new FormsAuthenticationWrapper();
person Eric Petroelje    schedule 22.06.2009
comment
Но разве this () не был бы конструктором производного класса, если бы он существовал, а не конструктором базового класса? Где в качестве base () быть конструктором базового класса? - person kervin; 22.06.2009
comment
вызов конструктора базового класса NO! this () НЕ вызывает конструктор базового класса! base () делает это. Пожалуйста, отредактируйте и исправьте. - person dss539; 22.06.2009
comment
@kervin & dss539 - Ой, совершенно неверно истолковал вопрос. Удалены вводящие в заблуждение сведения о вызове конструктора базового класса. - person Eric Petroelje; 22.06.2009

В ответ на Q2

Это перегружает конструктор.

Если означает, что звонит

Foo() 

это то же самое, что и звонок

Foo(null, null)
person Johnno Nolan    schedule 22.06.2009

Вопрос 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;
    }
person Tomas Aschan    schedule 22.06.2009
comment
ic хм Мне вроде как твой способ лучше, он имеет больше смысла с места в карьер и делает то же самое, правильно? - person chobo2; 22.06.2009
comment
Как вы можете видеть в ответе Нолдорина, примеры для вопроса 2 эквивалентны только в этом конкретном примере, но не по необходимости. - person Tomas Aschan; 23.06.2009

Вопрос 2

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

Конструктор без параметров для AuthenticationController вызовет конструктор, который принимает IFormsAuthentication и MembershipProvider, передавая два нулевых значения (это делается до того, как будет выполнен какой-либо код в блоке кода конструктора без параметров). Поскольку конструктор двух аргументов использует оператор объединения с нулевым значением (??) для присвоения переменных, а переданные аргументы имеют значение NULL, новый MembershipProvider используется вместе с объектом Membership.Provider.

Если бы этот конструктор не был явно определен, использовался бы конструктор без параметров по умолчанию. Это могло привести к неожиданному поведению, если был создан новый AuthenticationController (без передачи каких-либо аргументов конструктору), поскольку переменные-члены не были бы инициализированы.

person owst    schedule 22.06.2009

Вопрос 1:

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

равно:

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

Вопрос 2:

Он просто передает null аргументам конструктора formAuth и провайдера. ИМХО, это не лучшая практика. Лучше подойдет другой конструктор без аргументов.

ИЗМЕНИТЬ: это не имеет смысла. Извините, я очень торопился и даже не догадывался, что это конструктор, вызывающий другой конструктор.

У меня нет времени отвечать на вопрос 3 прямо сейчас, я вернусь к этому позже ...

person João Marcus    schedule 22.06.2009
comment
Ваш ответ на вопрос 2 немного неверен - это не только то же самое, что добавление другого конструктора. Когда вы связываете конструкторы в цепочку, как в примерах OP, фактически выполняются оба конструктора. В этом случае один из них оказывается пустым ... - person Tomas Aschan; 22.06.2009