Это означает «короткое замыкание или» . То есть если первая часть выражения ложна, используйте вторую. В Википедии есть статья об этом с синтаксисом для ряда языков.
Вы можете протестировать свои контроллеры и большую часть своего настраиваемого поставщика, преобразовав свой собственный код членства в два уровня: репозиторий доступа к данным, который взаимодействует только с базой данных, и уровень обслуживания, который использует репозиторий компоненты для предоставления 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. Итак, у вас есть три варианта.
К сожалению, вы не можете просто скопировать файл web.config или app.config и заставить его работать таким образом. Причина в том, что ваша сборка выполняется внутри процесса NUnit, а не под вашим приложением.
Чтобы исправить ситуацию, вам, вероятно, придется издеваться или заглушать членов членства, которым вы звоните, или следовать Соглашению over Подход к настройке параметров, которые вы сохранили в своем файле 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
Таким образом, ваш тест будет работать, и когда вы запустите его в своем приложении, он будет использовать любую конфигурацию настройки, которые вы сохранили.