2008-10-06 2 views
44

Я прочитал некоторые ответы здесь: тестирование представлений и контроллеров и насмешливость, но я до сих пор не могу понять, как тестировать контроллер ASP.NET MVC, который читает и устанавливает значения Session (или любые другие переменные на основе контекста .) Как предоставить (сеанс) контекст для моих методов тестирования? Издевается над нами? У кого-нибудь есть примеры? В принципе, я хотел бы подделать сеанс, прежде чем я вызову метод контроллера, и контроллер будет использовать этот сеанс. Есть идеи?ASP/NET MVC: Контрольные контроллеры с сеансами? Дразнящий?

ответ

43

Заканчивать пост Стивена Walther на Подделка контекста контроллера:

ASP.NET MVC Tip #12 – Faking the Controller Context

[TestMethod] 
public void TestSessionState() 
{ 
    // Create controller 
    var controller = new HomeController(); 


    // Create fake Controller Context 
    var sessionItems = new SessionStateItemCollection(); 
    sessionItems["item1"] = "wow!"; 
    controller.ControllerContext = new FakeControllerContext(controller, sessionItems); 
    var result = controller.TestSession() as ViewResult; 


    // Assert 
    Assert.AreEqual("wow!", result.ViewData["item1"]); 

    // Assert 
    Assert.AreEqual("cool!", controller.HttpContext.Session["item2"]); 
} 
12

Структура ASP.NET MVC не очень проста в обращении (или, скорее, требует слишком много настроек для правильной настройки и вызывает слишком много трения при тестировании, IMHO) из-за использования абстрактных базовых классов вместо интерфейсов. Нам удавалось писать абстракции для каждого запроса и сеансового хранилища. Мы сохраняем эти абстракции очень легкими, а затем наши контроллеры зависят от этих абстракций для каждого запроса или для хранения в сеансе.

Например, вот как мы управляем файлами auth. У нас есть ISecurityContext:

public interface ISecurityContext 
{ 
    bool IsAuthenticated { get; } 
    IIdentity CurrentIdentity { get; } 
    IPrincipal CurrentUser { get; set; } 
} 

С конкретной реализации, как:

public class SecurityContext : ISecurityContext 
{ 
    private readonly HttpContext _context; 

    public SecurityContext() 
    { 
     _context = HttpContext.Current; 
    } 

    public bool IsAuthenticated 
    { 
     get { return _context.Request.IsAuthenticated; } 
    } 

    public IIdentity CurrentIdentity 
    { 
     get { return _context.User.Identity; } 
    } 

    public IPrincipal CurrentUser 
    { 
     get { return _context.User; } 
     set { _context.User = value; } 
    } 
} 
2

Скотт Hanselman имеет пост о том, как create a file upload quickapp с MVC и обсуждает moking и конкретно касается «Как издеваются вещи, которые Арен «дружище».

+0

Хансельман на помощь ... – Codewerks 2008-10-07 01:37:04

5

Я нашел насмешливым, чтобы быть довольно простым. Вот пример издевательства над httpContextbase (содержащий объекты запроса, сеанса и ответа) с помощью moq.

[TestMethod] 
     public void HowTo_CheckSession_With_TennisApp() { 
      var request = new Mock<HttpRequestBase>(); 
      request.Expect(r => r.HttpMethod).Returns("GET");  

      var httpContext = new Mock<HttpContextBase>(); 
      var session = new Mock<HttpSessionStateBase>(); 

      httpContext.Expect(c => c.Request).Returns(request.Object); 
      httpContext.Expect(c => c.Session).Returns(session.Object); 

      session.Expect(c => c.Add("test", "something here"));    

      var playerController = new NewPlayerSignupController(); 
      memberController.ControllerContext = new ControllerContext(new RequestContext(httpContext.Object, new RouteData()), playerController);   

      session.VerifyAll(); // function is trying to add the desired item to the session in the constructor 
      //TODO: Add Assertions 
     } 

Надеюсь, что это поможет.

+2

Ого, это много работы, чтобы издеваться один метод :) Это явно «слишком много настроек «запах, и это результат использования абстрактных базовых классов, а не интерфейсов в качестве швов зависимостей. – chadmyers 2008-10-06 23:18:49

+1

Несомненно, я видел несколько проектов, в которых они установили код установки в класс «помощник» и использовали его снова и снова. – Korbin 2008-10-07 22:24:51

8

С MVC RC 1 ControllerContext обертывания HttpContext и выставляет его как свойство. Это делает насмешливым намного легче. Для того, чтобы издеваться переменным сеанс с Moq выполните следующие действия:

var controller = new HomeController(); 
var context = MockRepository.GenerateStub<ControllerContext>(); 
context.Expect(x => x.HttpContext.Session["MyKey"]).Return("MyValue"); 
controller.ControllerContext = context; 

См Scott Gu's post для более подробной информации.

0

Поскольку HttpContext является статическим, я использую Typemock Isolator, чтобы издеваться над ним, Typemock также имеет надстройку, созданную для ASP.NET unit testing, называемую Ivonna.

2

Я использовал следующее решение - создание контроллера, из которого наследуются все мои другие контроллеры.

public class TestableController : Controller 
{ 

    public new HttpSessionStateBase Session 
    { 
     get 
     { 
      if (session == null) 
      { 
       session = base.Session ?? new CustomSession(); 
      } 
      return session; 
     } 
    } 
    private HttpSessionStateBase session; 

    public class CustomSession : HttpSessionStateBase 
    { 

     private readonly Dictionary<string, object> dictionary; 

     public CustomSession() 
     { 
      dictionary = new Dictionary<string, object>(); 
     } 

     public override object this[string name] 
     { 
      get 
      { 
       if (dictionary.ContainsKey(name)) 
       { 
        return dictionary[name]; 
       } else 
       { 
        return null; 
       } 
      } 
      set 
      { 
       if (!dictionary.ContainsKey(name)) 
       { 
        dictionary.Add(name, value); 
       } 
       else 
       { 
        dictionary[name] = value; 
       } 
      } 
     } 

     //TODO: implement other methods here as needed to forefil the needs of the Session object. the above implementation was fine for my needs. 

    } 

} 

Затем используйте код следующим образом:

public class MyController : TestableController { } 
Смежные вопросы