Листинг 8.2. Фрагмент кода класса Accountcontroller
public class Accountcontroller : Controller
{
public Accountcontroller() : this(null, null)
{
}
public AccountController(IFormsAuthentication formsAuth,
IMembershipService service)
{
FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService();
}
public IFormsAuthentication FormsAuth
{
get;
private set;
}
public IMembershipService MembershipService
{
get;
private set;
}
}
Поскольку в контроллере Accountcontroller используются компоненты инфраструктуры ASP.NET, отвечающие за аутентификацию пользователей и управление учетными записями, для них созданы специальные обертки, приведенные в листинге 8.3, реализующие интерфейсы IFormsAuthentication и IMembershipService.
Листинг 8.3. Интерфейсы и реализация поддержки базовых служб
public interface IFormsAuthentication
{
void SignIn(string userName, bool createPersistentcookie);
void SignOut();
}
public class FormsAuthenticationService : IFormsAuthentication
{
public void SignIn(string userName, bool createPersistentCookie)
{
FormsAuthentication.SetAuthCookie(userName,
createPersistentCookie);
}
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
public interface IMembershipService
{
int MinPasswordLength { get; }
bool ValidateUser(string userName, string password);
MembershipCreateStatus CreateUser(string userName,
string password, string email);
bool ChangePassword(string userName,
string oldPassword, string newPassword);
}
public class AccountMembershipService : IMembershipService
{
private MembershipProvider _provider;
public AccountMembershipService()
: this(null)
{
}
public AccountMembershipService(MembershipProvider provider)
{
_provider = provider ?? Membership.Provider;
}
public int MinPasswordLength
{
get
{
return _provider.MinRequiredPasswordLength;
}
}
public bool ValidateUser(string userName, string password)
{
return _provider.ValidateUser(userName, password);
}
public MembershipCreateStatus CreateUser(string userName,
string password, string email)
{
MembershipCreateStatus status;
_provider.CreateUser(userName, password,
email, null, null, true, null, out status);
return status;
}
public bool ChangePassword(string userName,
string oldPassword, string newPassword)
{
MembershipUser currentUser = _provider.GetUser(
userName, true);
return currentUser.ChangePassword(oldPassword, newPassword);
}
}
Выделение обертки над службами ASP.NET позволяет не только получить возможность простого тестирования, но и заменять реализацию в процессе развития приложения. Например, при замене стандартного провайдера Membership на собственную реализацию или изменение провайдера авторизации для поддержки сертификатов, или Windows-аутентификация. Однако в целях тестирования этот подход просто незаменим.
Во время работы веб-приложения, созданного из шаблона, используется стандартная фабрика контроллеров, не передающая дополнительных параметров конструктору класса Accountcontroller, в тестовом же окружении используется имитация — код классов, имитирующих бурную деятельность. Пример классов, реализующих подход имитации, приведен в листинге 8.4.
Листинг 8.4. Классы для имитации в условиях тестирования
public class MockFormsAuthenticationService :
IFormsAuthentication
{
public void SignIn(string userName,
bool createPersistentcookie) { }
public void SignOut() { }
}
public class MockHttpContext : HttpContextBase
{
private IPrincipal _user;
public override IPrincipal User
{
get
{
if (_user == null)
{
_user = new MockPrincipal();
}
return _user;
}
set
{
_user = value;
}
}
}
Аналогично коду, приведенному в листинге 8.4, строятся остальные классы, используемые для тестирования. Основной принцип их создания состоит в том, чтобы развязать процесс тестирования с реальным функционалом — вместо работы с базами данных не делать ничего, либо возвращать стандартные значения, используемые для тестирования, и т. п.
Пример использования имитирующих классов в целях тестирования приведен в листинге 8.5. Процесс подготовки объектов выделен в отдельный метод GetAccountController() .
Листинг 8.5. Несколько методов для тестирования AccountController
private static AccountController GetAccountController()
{
IFormsAuthentication formsAuth = new
MockFormsAuthenticationService();
MembershipProvider membershipProvider = new MockMembershipProvider();
AccountMembershipService membershipService = new
AccountMembershipService(membershipProvider);
AccountController controller = new
AccountController(formsAuth, membershipService);
ControllerContext controllerContext = new
ControllerContext(new MockHttpContext(),
new RouteData(),
controller);
controller.ControllerContext = controllerContext;
return controller;
}
[Test]
public void RegisterPostReturnsViewIfPasswordIsNull()
{
AccountController controller = GetAccountController();
ViewResult result = (ViewResult)controller.Register("username",
"email", null, null);
Assert.AreEqual(6, result.ViewData["PasswordLength"]);
Assert.AreEqual(
"You must specify a password of 6 or more characters.",
result.ViewData.ModelState["password"].Errors[0].ErrorMessage);
}
[Test]
public void
RegisterPostReturnsViewIfNewPasswordDoesNotMatchConfirmPassword()
{
AccountController controller = GetAccountController();
ViewResult result = (ViewResult)controller.Register("username",
"email", "password", "password2");
Assert.AreEqual (6, result.ViewData["PassworcLLength"] );
Assert.AreEqual(
"The new password and confirmation password do not match.",
result.ViewData.ModelState["_FORM"].Errors[0].ErrorMessage);
}
[Test]
public void RegisterPostReturnsViewIfPasswordIsTooShort()
{
AccountController controller = GetAccountController();
ViewResult result = (ViewResult)controller.Register("username",