Модульное тестирование пользовательского MembershipProvider.ValidateUser с использованием кода в Global.asax

У меня есть модульный тест для проверки моего метода AccountController.LogIn. Результат перенаправления возвращается, чтобы указать на успех, в противном случае возвращается результат просмотра.

Тест всегда терпит неудачу, поскольку типом возвращаемого значения всегда является viewresult, хотя тест должен возвращать успех, поскольку учетные данные действительны, однако я не могу определить, в чем проблема. Мой TestMethod: CustomerRepositoryTest.cs

[TestMethod]
public void Can_Login_With_Valid_Credentials()
{
     //  Arrange
     Mock<IAddressRepository> mockAddressRepository = new Mock<IAddressRepository>();
     Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>();
     Mock<IOrderRepository> mockOrderRepository = new Mock<IOrderRepository>();

     LoginViewModel model = new LoginViewModel
     {
         Email = "[email protected]",
         Password = "password"
     };

     AccountController target = new AccountController(mockCustomerRepository.Object, mockAddressRepository.Object, mockOrderRepository.Object);

   //  Act
    ActionResult result = target.LogIn(model);

    //  Assert
    Assert.IsInstanceOfType(result, typeof(RedirectResult));
    Assert.AreEqual("", ((RedirectResult)result).Url);
    }

Когда я запускаю тест, он не работает в методе входа в My AccountController, когда я вызываю ValidateUser AccountController.cs

if (Membership.ValidateUser(LoginModel.Email, LoginModel.Password))
{
  ...
 return RedirectToRoute(new
 {
    controller = "Account",
    action = "Details"
 });
}
else
{
   return View();
}

Мой пользовательский MembershipProvider ValidateUser выглядит так:

AccountMembershipProvider.cs

public class AccountMembershipProvider : MembershipProvider
{
     [Inject]
    public ICustomerRepository repository { get; set; }

    public override bool ValidateUser(string username, string password)
    {
       var cust = repository.GetAllCustomers().SingleOrDefault..

Когда я запускаю приложение в обычном режиме, т.е. не тестирую, логин работает нормально. В приложении я добавляю CustomerRepository в настраиваемый поставщик членства в Global.asax:

    public class MvcApplication : System.Web.HttpApplication
    {
      private IKernel _kernel = new StandardKernel(new MyNinjectModules());

      internal class MyNinjectModules : NinjectModule
      {
        public override void Load()
        {
            Bind<ICustomerRepository>().To<CustomerRepository>();
        }
      }

      protected void Application_Start()

  {
   _kernel.Inject(Membership.Provider);
   ...

Может ли случиться так, что код Global.asax не запускается во время модульного тестирования? и поэтому мой пользовательский провайдер не вводится, следовательно, сбой?

ОБНОВЛЕНИЕ Я издевался над своим классом Provider и передал ему фиктивный объект CustomerRepository.

Mock<AccountMembershipProvider> provider = new Mock<AccountMembershipProvider>();

provider.Object.repository = mockCustomerRepository.Object;

Затем я создал настройку для метода, который пытаюсь протестировать:

 mockCustomerRepository.Setup(m => m.IsValidLogin("[email protected]", "password")).Returns(true);

Но, к сожалению, у меня все равно каждый раз терпят неудачу. Чтобы ответить на вопрос о том, нужен ли мне для теста реальный объект или имитация - я не привередливый, я просто хочу, чтобы он работал прямо сейчас!

ОБНОВЛЕНИЕ 2

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

Membership.ValidateUser(LoginModel.Email, LoginModel.Password)

Membership.Provider относится к типу SqlMembershipProvider (который предположительно является типом по умолчанию), и, следовательно, проверка не выполняется.

Если я передаю поставщика своему настраиваемому провайдеру ...

((AccountMembershipProvider)Membership.Provider).ValidateUser(LoginModel.Email, LoginModel.Password)

При запуске теста я получаю исключение InvalidCastException. Похоже, что мой смоделированный AccountMembershipProvider не используется для теста, а вместо него используется поставщик по умолчанию.

Думаю, вы уже определили это в комментарии:

// set your mock provider in your AccountController

Однако я не уверен, что именно вы имеете в виду - у меня нет свойства на моем AccountController, которому можно было бы назначить поставщика, и я не ввожу его в конструктор.


person markpsmith    schedule 01.02.2012    source источник
comment
Чтобы вы знали, я добавил дополнительную информацию к своему ответу ранее в тот же день, отредактировав. Не стесняйтесь обращаться за разъяснениями или более конкретной информацией.   -  person Gilles    schedule 02.02.2012
comment
@Gilles - Ваши предложения я реализовал (см. Обновление). Они многое мне прояснили, хотя, к сожалению, каждый раз терпят неудачу! Нужно ли мне определять методы настройки как для классов Provider, так и для Repository?   -  person markpsmith    schedule 02.02.2012
comment
Я обновляю свой пост примером ... В принципе, если ваш провайдер является имитацией, нет необходимости создавать имитацию для его зависимостей, поскольку фиктивный объект не будет их вызывать.   -  person Gilles    schedule 02.02.2012
comment
@Gilles - Смотрите обновления. В тесте по-прежнему используется поставщик по умолчанию, а не мой собственный. Спасибо за вашу помощь.   -  person markpsmith    schedule 03.02.2012
comment
Является ли ValidateUser статическим методом в Membership.ValidateUser?   -  person Gilles    schedule 03.02.2012
comment
@Gilles - Нет, это публичное переопределение bool.   -  person markpsmith    schedule 03.02.2012
comment
Поскольку M в членстве было написано с заглавной буквы, я предположил, что это может быть статический метод для членства. Поскольку это не статический метод, как членство определяется / объявляется в вашем AccountController?   -  person Gilles    schedule 03.02.2012
comment
@Gilles - Ах, да, я понимаю, что вы имеете в виду. В членстве (т.е. базовом классе) это статический метод, я неправильно понял - извинения.   -  person markpsmith    schedule 03.02.2012
comment
Это будет проблемой, поскольку Moq не может работать со статическими методами. Это ограничение Moq. Это означает, что вы не сможете имитировать вызов ValidateUser. Возможно, вы все еще сможете имитировать репозиторий, используемый в вызове ValidateUser.   -  person Gilles    schedule 04.02.2012


Ответы (2)


Ваш исходный вопрос: «Это тот случай, когда код Global.asax не запускается во время модульного тестирования? и поэтому мой настраиваемый поставщик не внедряется, следовательно, сбой ? "

Мой ответ:

да.

Файл global.asax используется ASP.Net и ISS во время выполнения. Он компилируется, когда сервер получает свой первый запрос.

Когда вы тестируете, вы находитесь не в контексте веб-приложения ASP.Net, работающего в ISS, а скорее в программе, выполняющейся в тестовом сеансе. Это означает, что ваш global.asax не будет вызван.

Что еще более важно, когда вы звоните:

Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>();

Ninject не будет называться заполнением импорта. Moq создаст макет на основе интерфейса. Никакой реальный объект не будет создан.

Поскольку вы не определили никаких методов установки, то есть:

mockCustomerRepository.Setup(mock => mock.MyAuthenticateMethod()).Returns(true);

Вы передаете имитацию без определенного поведения. По умолчанию они возвращают false. Это, вероятно, объясняет, почему вы всегда получаете результат просмотра.

Что вам нужно сделать, так это определить методы настройки для методов, которые вам нужно имитировать.

Это методы CustomerRepository, которые будут вызываться при вызове:

target.LogIn(model);

Также обратите внимание, что ваш AccountMembershipProvider не получит его CustomerRepository, поскольку NInject не будет использоваться. Если вы тестируете AccountController, а он не статичен (Moq не работает со статикой), вам следует подумать о насмешке над AccountMembershipProvider. Если вы не можете этого сделать, вам нужно будет предоставить свой фиктивный экземпляр CustomerRepository в AccountMembershipProvider.repository в ваших тестах.

Другое решение: вместо создания макета Moq вы также можете вручную создать (с новым) реальный экземпляр CustomerRepository в своем тесте.

Вы могли бы заставить Ninject сделать это, но почему? Вы можете создать его сами и знаете, какой именно тип создавать.

Все сводится к тому, нужен ли вам макет или реальный экземпляр объекта для этого теста.

Обновление:

Если ваш провайдер подделан, то репозиторий на нем настраивать не нужно. Когда вы вызываете макет, реальный объект не вызывается.

Что вам нужно сделать, это примерно так:

Mock<AccountMembershipProvider> provider = new Mock<AccountMembershipProvider>();
// set your mock provider in your AccountController

provider.Setup(m => m.ValidateUser("[email protected]", "password")).Returns(true);

Обновление 2:

Я думаю, вы довольно близки к тому, чтобы ваш тест заработал. Не видя весь ваш код (тест и тестируемый класс), я больше не могу вам помочь. Мне также кажется, что я ответил на ваш исходный вопрос, но если вы все еще не можете решить эту проблему, вы можете получить дополнительную помощь, задав новый вопрос, связанный с проблемой, которую вы сейчас решаете.

person Gilles    schedule 01.02.2012
comment
В моем последнем редактировании я добавил дополнительную информацию в ответ. Новые части выделены курсивом. - person Gilles; 01.02.2012
comment
Я чувствую, что на мой первоначальный вопрос действительно был дан ответ, и тот факт, что вы сделали все возможное, просто предоставив ответ, является вашей чести. - person markpsmith; 06.02.2012
comment
@Gilles, вы сказали, // установите своего ложного провайдера в свой AccountController, не могли бы вы дать мне пример того, как установить провайдера на контроллер - person Artur Keyan; 18.01.2015

В вашем коде вы только создаете контроллер, но не запускаете инициализацию членства. Я рекомендую вам создать свой собственный UserService с помощью метода ValidateUser и других, которые вам нужны, вместо использования статического членства в классе.

person Evgeny Levin    schedule 01.02.2012