Как проверить аутентификацию ASP.NET MVC 3 при использовании членства - PullRequest
8 голосов
/ 05 февраля 2012

Я хочу проверить свои AccountController. Проблема в том, что в методе Register я использую следующую строку для создания пользователя:

Membership.CreateUser(model.Email, model.Password, model.Email, null, null, true, null, out createStatus);

В веб-приложении я использую CustomMembershipProvider, который я установил с помощью web.config. В моём модульном тесте класс членства в стандарте SqlMembershipProvider. И не мой CustomMembershipProvider, который я использую в своем приложении.

Как настроить членство в контексте модульного теста? Я имею в виду настроить его программно, так как asp net устанавливает его после прочтения файла web.config.

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

public void RegisterTest()
{
    IUsersManager repository = new Tests.Data.UsersManager();
    AccountController target = new AccountController(repository); 
    //be able to set Membership underlying provider
    Membership.Provider = new MockMembershipProvider();
}

Ответы [ 3 ]

8 голосов
/ 06 февраля 2012

Определите интерфейс членства, что-то вроде этого:

public interface IMembershipProvider
{
    void CreateUser(string username, string password);
}

... реализовать его для вашего MVC-приложения следующим образом:

public class AspDotNetMembershipProvider : IMembershipProvider
{
    public void CreateUser(string username, string password)
    {
        string createStatus;

        Membership.CreateUser(
            username,
            password,
            username,
            null,
            null,
            true,
            null,
            out createStatus);

        // throw an exception if createStatus isn't as expected
    }
}

... затем введите его в свой контроллер и используйте его так:

public class AccountController
{
    private readonly IMembershipProvider _membershipProvider;

    public AccountController(IMembershipProvider membershipProvider)
    {
        this._membershipProvider = membershipProvider;
    }

    public ActionResult Register(RegistrationModel model)
    {
        // Try and catch this, returning a success ActionResult if it worked:
        this._membershipProvider.CreateUser(model.Email, model.Password);
    }
}

ASP.NET для ряда вещей использует статические классы, такие как Membership, но доступ к статическим классам всегда затрудняет модульное тестирование. Стандартное решение - определить интерфейс для службы, реализовать его с помощью статического класса ASP.NET и внедрить его в свои контроллеры.

Вы можете настроить впрыск (если вы этого еще не сделали), используя DependencyResolver по умолчанию и контейнер DI, например Unity .

2 голосов
/ 06 февраля 2012

Я создал более структурированного поставщика членства, который был разбит на три различных интерфейса (и поставщик разрешает их, используя DependencyResolver).

Это облегчает модульное тестирование провайдера. Просто протестируйте вашу реализацию IAccountRepository.

Вы можете прочитать об этом здесь: http://blog.gauffin.org/2011/09/a-more-structured-membershipprovider/

или просто установите пакет nuget:

install-package griffin.mvccontrib
0 голосов
/ 06 февраля 2012

Ваш модульный тест контроллера не должен использовать ваш CustomMembershipProvider или стандартный поставщик SQL. При выполнении модульного теста вы должны использовать Stub / Fake, чтобы иметь полный контроль над тем, что он возвращает. Если в тесте вы используете реального провайдера, он больше не является модульным тестом, это интеграционный тест.

Чтобы достичь тестируемости юнитов, вам нужно определить провайдера подделки, либо с помощью фреймворка Mocking, либо вручную подбрасывая фальшивку с «консервированными» результатами. Подделка может выглядеть примерно так:

public class FakeMembershipProvider : MembershipProvider
{
   public MembershipCreateStatus CreateStatus = MembershipCreateStatus.Success;

   public void CreateUser((string username, string password, string email, 
       string passwordQuestion, string passwordAnswer, bool isApproved, 
       object providerUserKey, out MembershipCreateStatus status)
   {
      status = CreateStatus;
   }

   ...
}

Пусть контроллер принимает провайдера в качестве аргумента ctor

public class AccountController
{
    private readonly MembershipProvider _membershipProvider;

    public AccountController(MembershipProvider membershipProvider)
    {
        _membershipProvider = membershipProvider;
    }

    public ActionResult Register(RegistrationModel model)
    {
        MembershipCreateStatus result;
        _membershipProvider.CreateUser(model.Email, model.Password, ..., out result);

        return View(/*Make this depend on the result*/);
    }
}

В модульном тесте вы хотите настроить подделку в соответствии с тем, что вы хотите проверить, и вы можете утверждать результат, который вы ожидаете для каждого результата регистрации:

[Test]
void Should_display_success_view_when_user_successfully_created()
{
    var membershipProvider = new FakeMembershipProvider();
    membershipProvider.CreateStatus = MembershipCreateStatus.Success;
    var controller = new AccountController(membershipProvider);
    var model = new RegistrationModel();

    var result = controller.Register(model) as ViewResult;

    Assert.That(result.Name, Is.EqualTo("ExpectedViewName"));     
}

Поскольку MemberShipProvider довольно велик, и вы, вероятно, не собираетесь использовать все его, разумно использовать подход @SteveWilkes, заключающийся в обертывании класса Membership, чтобы создать меньший и более целевой интерфейс. Кроме того, насмешливый фреймворк сэкономит вам много работы. Чтобы связать ваш контроллер с новой зависимостью, вы должны создать новую ControllerFactory.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...