Как пройти тестирование членства в Asp.net?

Я новичок в модульном тестировании, и я пытаюсь протестировать некоторые из моих материалов.NET, которые я пишу.

Поэтому я пытаюсь проверить мой VerifyUser метод, который проверяет, действительны ли учетные данные пользователей.

Так вот как это выглядит:

public bool VerifyUser(string userName, string password)
    {
        bool valid = Membership.ValidateUser(userName, password);
        return valid;
    }

И теперь каждый раз, когда я запускаю свой модульный тест, он не проходит. Я знаю, что я передаю правильные полномочия и прочее. Затем до меня дошло, что, возможно, мой тестовый проект (который находится в том же решении, что и мой реальный проект) может нуждаться в собственном web.config файл со строкой подключения и прочее. Или файл конфигурации приложения, может быть, так как это проект библиотеки приложений.

Так что я просто скопировать web.config файл из моего реального проекта и назвать его день? Или я должен только брать части от этого? Или я просто далеко.

Моя база данных использует пользовательскую базу данных с членством.net, объединенным с моей базой данных. Поэтому в моем конфигурационном файле мне нужно было указать ManagerProvider и roleProvider.

Вот так выглядит мой юнит тест

   [Test]
   public void TestVerifyUser()
   {
      AuthenticateUser authenitcate = new AuthenticateUser();
      bool vaild = authenitcate.VerifyUser("chobo3", "1234567");


      Assert.That(vaild, Is.True);
   }

Также позже у меня есть один из моих методов asp.net mvc ActionResult (если быть точным, представление входа в систему) У меня есть это:

FormsAuthentication.RedirectFromLoginPage (loginValidation.UserName, запомнить Me);

Так что теперь, как я могу написать модульный тест, который будет делать то, что будет делать пользователь. Допустим, они начинаются с домашней страницы, затем нажимают на страницу входа и успешно входят в систему. Я хочу, чтобы они были перенаправлены на домашнюю страницу.

Я не уверен, как представить это в коде. Я уверен, что RedirectFromLoginPage работает, и вот что я сейчас тестирую. Я проверяю тот факт, что у меня есть 3 вещи, которые могут произойти при входе в систему ActionResult метод.

  1. Пользователь входит в систему и получает обратно туда, откуда он пришел.
  2. Пользователь не может войти в систему и отправляется обратно в LoginView и видит сообщения об ошибках.
  3. Пользователь попытался перейти на безопасный и был перенаправлен на страницу входа. Если логин успешно, он будет перенаправлен обратно на безопасную страницу через ReturnUrl.

Поэтому я хочу сделать тест, чтобы увидеть, работают ли они так, как должны. Вот почему мне нужно, чтобы пользователь заходил с домашней страницы, чтобы увидеть, не перенаправят ли они его обратно на нее позже, и если они зайдут с защищенной страницы, они перенаправят на нее позже.

Я также кстати использую NUnit 2.5 и VS2008 Pro.


Это то, что я пытаюсь проверить. Я нахожусь в той части, где я пытаюсь увидеть, является ли пользователь действительным или нет (оператор if). Понятия не имею, как это проверить.

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
                {

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

            }


            return View("Login");
        }

3 ответа

Решение

Вы можете протестировать свои контроллеры и большую часть своего пользовательского провайдера путем рефакторинга своего пользовательского кода членства в два уровня: хранилище доступа к данным, которое взаимодействует только с базой данных, и уровень обслуживания, который использует компоненты хранилища для предоставления API членства. Сервисный уровень - это место, где вы должны проверять аргументы, хранить и применять параметры, такие как EnablePasswordReset, и преобразовывать любые исключения базы данных или коды состояния в форму, подходящую для использования контроллером.

Когда вы указываете каждый уровень со своим собственным интерфейсом, потребители могут писать в этот интерфейс независимо от того, как он реализован. Когда ваше приложение работает, ваш провайдер, конечно, общается с базой данных через эти интерфейсы, но для тестирования вы можете смоделировать интерфейсы хранилища или службы. Вы можете протестировать свой сервисный уровень, посмеявшись над уровнем хранилища, не связываясь с базой данных или файлом web.config, и вы можете протестировать свои контроллеры, посмеявшись над сервисным уровнем. Если вы не хотите проводить рефакторинг всего провайдера, вы все равно можете протестировать свои контроллеры, если вы только создадите интерфейс службы и попросите его использовать ваши контроллеры.

Если быть точным, то ваш интерфейс хранилища и службы может выглядеть примерно так:

namespace Domain.Abstract {
    public interface IRepository {
        string ConnectionString { get; }
    }
}

namespace Domain.Abstract {
    public interface IUserRepository : IRepository {
        MembershipUser CreateUser(Guid userId, string userName, string password, PasswordFormat passwordFormat, string passwordSalt,
                string email, string passwordQuestion, string passwordAnswer, bool isApproved,
                DateTime currentTimeUtc, bool uniqueEmail);
        MembershipUser GetUser(Guid userId, bool updateLastActivity, DateTime currentTimeUtc);
        PasswordData GetPasswordData(Guid userId, bool updateLastLoginActivity, DateTime currentTimeUtc);
        void UpdatePasswordStatus(Guid userId, bool isAuthenticated, int maxInvalidPasswordAttempts, int passwordAttemptWindow, 
                      DateTime currentTimeUtc, bool updateLastLoginActivity, DateTime lastLoginDate, DateTime lastActivityDate);
        //....
    }
}

namespace Domain.Abstract {
  public interface IUserService {
    bool EnablePasswordRetrieval { get; }
    bool EnablePasswordReset { get; }
    bool RequiresQuestionAndAnswer { get; }
    bool RequiresUniqueEmail { get; }
    //....

    MembershipUser CreateUser(string applicationName, string userName, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved);
    MembershipUser GetUser(Guid userId, bool userIsOnline);
    bool ValidateUser(Guid userId, string password);
    //...
    }
}

namespace Domain.Concrete {
  public class UserService : IUserService {
    private IUserRepository _userRepository;


    public UserService(IUserRepository userRepository) {
        _userRepository = userRepository;
    }
    //...
    public bool ValidateUser(Guid userId, string password) {
        // validate applicationName and password here
        bool ret = false;
        try {
            PasswordData passwordData;
            ret = CheckPassword(userId, true, true, DateTime.UtcNow, out passwordData);
        }
        catch (ObjectLockedException e) {
            throw new RulesException("userName", Resource.User_AccountLockOut);
        }
        return ret;
    }

    private bool CheckPassword(Guid userId, string password, bool updateLastLoginActivityDate, bool failIfNotApproved,
                                DateTime currentTimeUtc, out PasswordData passwordData) {
        passwordData = _userRepository.GetPasswordData(userId, updateLastLoginActivityDate, currentTimeUtc);

        if (!passwordData.IsApproved && failIfNotApproved)
            return false;

        string encodedPassword = EncodePassword(password, passwordData.PasswordFormat, passwordData.PasswordSalt);
        bool isAuthenticated = passwordData.Password.Equals(encodedPassword);

        if (isAuthenticated && passwordData.FailedPasswordAttemptCount == 0 && passwordData.FailedPasswordAnswerAttemptCount == 0)
            return true;
        _userRepository.UpdatePasswordStatus(userId, isAuthenticated, _maxInvalidPasswordAttempts, _passwordAttemptWindow,
                                            currentTimeUtc, updateLastLoginActivityDate,
                                            isAuthenticated ? currentTimeUtc : passwordData.LastLoginDate,
                                            isAuthenticated ? currentTimeUtc : passwordData.LastActivityDate);

        return isAuthenticated;
    }
}

Система членства Asp.Net разработана для работы в контексте запроса Asp.Net. Итак, у вас есть три варианта здесь.

  1. Большинство людей, сталкивающихся с такой зависимостью, пишут вокруг нее тонкую оболочку. Оболочка ничего не делает, просто перенаправляет все вызовы на основную зависимость. Так что они просто не проверяют это. Ваш AuthenticateUser - такая обертка. Вероятно, вы должны сделать все методы виртуальными или извлечь интерфейс, чтобы сделать его смешным, но это уже другая история.
  2. Используйте TypeMock Isolator и макет Членство.
  3. Используйте инфраструктуру Ivonna и запустите свой тест в контексте Asp.Net (это будет интеграционный тест).

К сожалению, вы не можете просто скопировать ваш web.config или app.config и заставить его работать таким образом. Причина в том, что ваша сборка выполняется внутри процесса NUnit, а не под вашим приложением.

Чтобы исправить вашу ситуацию, вам, вероятно, придется использовать Mock или Stub подписчиков, которым вы звоните, или следовать подходу "Соглашение о конфигурации" с настройками, которые вы сохранили в своем файле web.config.

Есть много фальшивых фреймворков, но вот пара: Rhino Mocks, Moq

Кроме того, чтобы следовать соглашению об использовании конфигурации, вы можете сделать что-то вроде этого:

static ConfigurationSettings
{
     static String SomeSetting
     {
        get
        {
           var result = "HARDCODEDVALUE";
           if (ConfigurationManager.AppSettings["SOMEKEY"] != null)
               result = ConfigurationManager.AppSettings["SOMEKEY"];
           return result;
     }
}

Затем вы можете использовать этот код следующим образом:

//this is how the old code might look
var mySetting = ConfigurationManager.AppSettings["SOMEKEY"];
//use the setting

//this is how the new code would look
var mySetting = ConfigurationSettings.SomeSetting;
//use the setting   

Таким образом, ваш тест будет работать, и когда вы запустите его под своим приложением, он будет использовать любые параметры конфигурации, которые вы сохранили.

Другие вопросы по тегам