2009-06-19 3 views
15

Я новичок в модульном тестировании, и я пытаюсь проверить часть своего членства в .NET, которое я пишу.Как тестировать членство в Asp.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, memMe);

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

Я не уверен, как представить это в коде. Я почти уверен, что работает 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"); 
     } 

ответ

7

Вы можете протестировать свои контроллеры и большую часть своего пользовательского провайдера, реорганизовывая свой код членства на два уровня: репозиторий доступа к данным, который взаимодействует только с базой данных, и уровень сервиса, который использует компоненты репозитория для предоставления 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; 
    } 
} 
1

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

Чтобы исправить вашу ситуацию, вам, вероятно, придется выполнять Mock или Stub членов членства, которые вы вызываете, или следовать подходу Console over Configuration к настройкам, которые вы сохранили в своем 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 

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

+0

К сожалению, я не понимаю «соглашение по конфигурации подхода» я прочитал о насмехаясь в книга у меня есть.Это как-то связано с использованием интерфейсов или что-то в этом роде? Я нашел эту ссылку, но я думаю, что это не сработает для меня, так как я использую Nunit, и он использует MbUnit? http://aspalliance.com/1590 – chobo2

+0

@ chobo2 Я расскажу о том, как его использовать. Разумные фреймворки определенно имеют склонность к использованию интерфейсов и не без оснований. Использование интерфейсов обеспечивает хорошую развязку. Однако вам не нужно использовать интерфейсы во всех насмешливых фреймворках. Кроме того, NUnit и MbUnit похожи во многих отношениях, поэтому что-то, что сделано в MbUnit, скорее всего, имеет параллель с NUnit. Стоит делать некоторые исследования. – Joseph

+0

Хорошо Спасибо. Я до сих пор не понимаю интерфейсы и действительно не знаю, когда дело доходит до модульного тестирования. Так что, надеюсь, одна из этих фреймворков сделает это немного легче. Какой из них вы рекомендуете? Я вижу, что «Moq» использует linq sytanx, поэтому я не уверен, правильно ли это, если это будет полезно для меня, поскольку я мало знаю о linq. Также как эти рамки работают с nunit? Как я все еще использую nunit для запуска моего теста или что? – chobo2

3

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

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