Как пройти тестирование членства в 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
метод.
- Пользователь входит в систему и получает обратно туда, откуда он пришел.
- Пользователь не может войти в систему и отправляется обратно в
LoginView
и видит сообщения об ошибках. - Пользователь попытался перейти на безопасный и был перенаправлен на страницу входа. Если логин успешно, он будет перенаправлен обратно на безопасную страницу через 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. Итак, у вас есть три варианта здесь.
- Большинство людей, сталкивающихся с такой зависимостью, пишут вокруг нее тонкую оболочку. Оболочка ничего не делает, просто перенаправляет все вызовы на основную зависимость. Так что они просто не проверяют это. Ваш AuthenticateUser - такая обертка. Вероятно, вы должны сделать все методы виртуальными или извлечь интерфейс, чтобы сделать его смешным, но это уже другая история.
- Используйте TypeMock Isolator и макет Членство.
- Используйте инфраструктуру 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
Таким образом, ваш тест будет работать, и когда вы запустите его под своим приложением, он будет использовать любые параметры конфигурации, которые вы сохранили.