Тестирование ошибки выброса моего контроллера из-за HttpContext в .net MVC - PullRequest
2 голосов
/ 01 августа 2011

У меня есть контроллер, который вызывает HttpContext, например:

[Authorize(Roles = "Administrador")]
public class ApuradorController : Controller
{
    private readonly Questiona2011Context _context = new Questiona2011Context();

    private readonly AuthenticationService _authenticationService = new AuthenticationService();
}

HttpContext - это вызов в классе AuthenticationService:

public class AuthenticationService
{
    private IPrincipal _user = HttpContext.Current.User;

    ...
}

В моем проекте, где я тестирую контроллеры, когда я выполняю экземплярКонтроллер выдаёт ошибку в private IPrincipal _user = HttpContext.Current.User; Строка: Ссылка на объект не установлена ​​для экземпляра объекта.

Что мне нужно для тестирования моих контроллеров?

Ответы [ 5 ]

3 голосов
/ 01 августа 2011

Главное, что вам не хватает - это знание того, как разработать проект ASP.NET MVC для тестирования.

Вы должны спроектировать свой контроллер, чтобы использовать внедрение зависимостей. То есть контроллеры не должны использовать конкретную реализацию AuthenticationService, а использовать IAuthenticationService, конкретная реализация которого будет предоставлена ​​во время выполнения. На данный момент, когда контроллер создан, AuthenticationService также создается. Но в тестовом сценарии HttpContext имеет значение null, и создание AuthenticationService завершается с ошибкой NullReference. Если вы разрабатываете это через интерфейс, в целях тестирования вы будете предоставлять поддельную реализацию AuthenticationService контроллеру, и он не будет выдавать исключение.

public interface IAuthenticationService
{
    IPrincipal User {get;}
}

public class AuthenticationService : IAuthenticationService
{
    private IPrincipal _user = HttpContext.Current.User;

    ...
}

//the controller
[Authorize(Roles = "Administrador")]
public class ApuradorController : Controller
{
    private readonly Questiona2011Context _context = new Questiona2011Context();

    private readonly IAuthenticationService _authenticationService;

    public ApuradorController(IAuthenticationService authenticationService)
    {
         _authenticationService = authenticationService;
    }
}

В тестовом сценарии вы могли бы использовать некоторую библиотеку-макет для фальшивой реализации IAuthenticationService, например moq . И поставьте ценность для этого через насмешку

var mockAuthenticationService = new Mock<IAuthenticationService>();
//setup mockAuthenticationService

var controller = new ApuradorController(mockAuthenticationService.Object);

На этот раз он не будет генерировать исключения.

Информация, упомянутая выше, не поможет, если вы не понимаете принципов разработки проекта, проверяемого модулем. Для быстрого начала прочитайте эту ссылку. Для дальнейшего чтения, адресных книг о asp.net mvc, я бы рекомендовал их Стивену Сандерсону. Основная идея конструкции контроллера, тестируемого модулем, заключается в том, что у вас должна быть возможность поставлять поддельные компоненты в контроллер, поддельные репозитории, сервисы и т. Д. И оставлять реальную только ту часть контроллера, которая тестируется модулем. Затем протестируйте итерации контроллера с этими поддельными частями. Модульное тестирование означает тестирование этого взаимодействия. Если взаимодействия правильные, они будут правильными с реальными реализациями этих компонентов. Если они не правы, тест не пройден.

1 голос
/ 01 августа 2011

Как уже говорили другие, для модульного тестирования ваших контроллеров вам необходимо спроектировать их так, чтобы вы могли заменить во время выполнения HTTP-контекст (request, response и так далее), поскольку у вас не будет реального HTTP-контекста. когда юнит тестирование.

Еще одна вещь, о которой вам нужно знать, это то, что когда вы вызываете действие в вашем контроллере через модульный тест (скажем, ApuradorController.Index ()), вы не получите автоматически тот же конвейер выполнения, что ASP.NET MVC. дает вам время выполнения, и поэтому некоторые события, которые являются частью «нормального» выполнения, не будут запущены. Например, если вы выполняете какие-либо действия в OnActionExecuting, этот метод не будет автоматически запускаться при вызове ApuradorController.Index () в модульном тесте.

Изначально тестирование контроллеров может быть сложным, поскольку оно заставит вас изменить способ программирования. Чистый результат - лучший код, но это может оказаться сложной задачей.

1 голос
/ 01 августа 2011

Для меня это выглядит не совсем правильно, вы добавляете зависимость на своем служебном уровне в пространство имен System.Web. Лучше передать имя пользователя через сервисный уровень - возможно, в конструкторе и, возможно, лучше всего иметь его в конструкторе в базовом классе обслуживания, чтобы оно могло быть доступно во всех ваших методах обслуживания.

abstract class BaseService
{
    procteced IPrinciple _userName;

    public BaseService(IPrinciple userName)
    {
        _userName = userName;
     }
}

class AuthenticationService : BaseService
{
   public AuthenticationService(IPrinciple userName)
        :base(userName)
   {

   }
}

В контроллере:

AuthenticationService _service = new AuthenticationService(HttpContext.Current.User);

Возможно, вот так - если вы собираетесь получать доступ к таким вещам, как роли и т. Д., Вам может быть полезно создать небольшой класс-обертку вокруг классов членства ASP.net, который реализует интерфейс из сервисного уровня для таких вещей, как доступ к ролям / информации профиля.

0 голосов
/ 01 августа 2011

Вы можете подключить HttpContext (реальная вещь) и использовать его:

  // Arrange
  HttpContext.Current =
    new HttpContext(
      new HttpRequest( "", "http://tempuri.org", "" ),
      new HttpResponse( new StringWriter() ) );
  HttpContext.Current.User = new GenericPrincipal( new GenericIdentity("MyUser"), new[]{"Admin"} );

  // Call your controller action...

Лично я бы прошел лишнюю милю и добавил бы еще один уровень абстракции и создал бы что-то еще. как IPrincipalAccessor, см. все остальные ответы для более подробной информации.

0 голосов
/ 01 августа 2011

Вам нужно будет посмеяться над HttpContextBase, чтобы это работало. статья Хансельмана об этом может помочь вам.

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