Я представляю автоматическое тестирование с использованием NUnit, NSubstitute для проекта, который использует Ninject и общие хранилища.Является ли частичное издевательство над безопасностью хорошей практикой?
Для регрессионного тестирования я заменяю общие хранилища на память, чтобы предотвратить работу с базой данных.
Кроме того, для проверки ограничений безопасности услуг, я насмешливый службу безопасности, которая выглядит следующим образом:
public class SecurityService : ISecurityService
{
#region Properties
private IScopedDataAccess DataAccess { get; }
private IMappingService MappingService { get; }
#endregion
#region Constructor
public SecurityService(IScopedDataAccess scopedDataAccess, IMappingService mappingService)
{
DataAccess = scopedDataAccess;
MappingService = mappingService;
}
#endregion
#region Methods
public virtual string GetUsername()
{
return HttpContext.Current.User.Identity.Name;
}
public AppUserSecurityProfileServiceModel GetCurrentUserData()
{
var username = GetUsername();
var userDataModel = DataAccess.AppUserRepository.AllNoTracking.FirstOrDefault(u => u.Username == username);
if (userDataModel == null)
return null;
var ret = MappingService.Mapper.Map<AppUserSecurityProfileServiceModel>(userDataModel);
return ret;
}
public virtual int GetCurrentUserId()
{
var userData = GetCurrentUserData();
if (userData == null)
throw new SecurityException($"No user data could be fetched for - {GetUsername()}");
return userData.AppUserId;
}
public bool IsInRole(UserRoleEnum role, int? userId = null)
{
int actualUserId = userId ?? GetCurrentUserId();
var hasRole = DataAccess.AppUserXUserRoleRepository.AllNoTracking.Any(x => x.AppUserId == actualUserId && x.UserRoleId == (int) role);
return hasRole;
}
public bool CanPerformAction(UserActionEnum action, int? userId = null)
{
int actualUserId = userId ?? GetCurrentUserId();
var hasAction = DataAccess.AppUserXUserRoleRepository.AllNoTracking
.Where(x => x.AppUserId == actualUserId)
.Join(DataAccess.UserRoleRepository.AllNoTracking, xRole => xRole.UserRoleId, role => role.UserRoleId, (xRole, role) => role)
.Join(DataAccess.UserRoleXUserActionRepository.AllNoTracking, xRole => xRole.UserRoleId, xAction => xAction.UserRoleId,
(role, xAction) => xAction.UserActionId)
.Contains((int) action);
return hasAction;
}
// other methods can appear here in the future
#endregion
}
Каждый регрессионный тест симулирует текущего пользователя, как это:
public void FakeCurrentUser(int userId)
{
var userRef = DataAccess.AppUserRepository.AllNoTracking.FirstOrDefault(u => u.AppUserId == userId);
var securitySubstitude = Substitute.ForPartsOf<SecurityService>(Kernel.Get<IScopedDataAccess>(), Kernel.Get<IMappingService>());
securitySubstitude.When(x => x.GetUsername()).DoNotCallBase();
securitySubstitude.GetUsername().Returns(userRef?.Username ?? "<none>");
securitySubstitude.When(x => x.GetCurrentUserId()).DoNotCallBase();
securitySubstitude.GetCurrentUserId().Returns(userId);
Kernel.Rebind<ISecurityService>().ToConstant(securitySubstitude);
}
В основном, это позаботится о замене методов, основанных на контексте (то есть HttpContext
в моем случае), но оставляет другой метод неповрежденным.
Каждая тестируемая служба будет создана после этой инициализации, поэтому я уверен, что соответствующий экземпляр вводится.
Вопрос: это нормально, чтобы издеваться над этим сервисом, или это анти-шаблон?
Да, я могу внести любые изменения в код. Наличие отдельной зависимости - очень хорошая идея, так как я буду точно издеваться над тем, что порождает контекст. Все остальное в зависимости от безопасности может быть «реальным». Благодарю. – Alexei