Кажется, не может установить объект при насмешливом HttpApplicationState с Moq
У меня есть фильтр действий, который устанавливает объект в HttpApplicationState HttpContext контекста фильтра. Я хотел бы иметь эту функциональность в модульном тесте, но по какой-то причине объект не устанавливается в базовом NameObjectCollectionBase, из которого происходит HttpApplicationState.
Я знаю, что функциональность работает, потому что когда я запускаю приложение MVC, оно работает как положено.
Как я могу настроить свой тест, чтобы заставить его устанавливать объекты в состоянии приложения? Я использую Moq и вот часть кода до сих пор. Это терпит неудачу на
Asset.IsNotNull(context.HttpContext.Application["config"]);
Вот код
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ControllerBase controller = filterContext.Controller;
if (!(controller is ApplicationController))
return;
ApplicationController applicationController = (ApplicationController) controller;
IDictionary<string, string> config;
// Loads the view configuration values.
if (filterContext.HttpContext.Application["config"] == null)
{
config = applicationController.ApplicationService.GetConfiguration();
filterContext.HttpContext.Application["config"] = config;
}
else
{
config = (IDictionary<string, string>) filterContext.HttpContext.Application["config"];
}
applicationController.ViewBag.BlogTitle = AddConfigurationValueToViewBag("BlogTitle", config);
}
Вот тест до сих пор.
[TestMethod]
public void ApplicationAttribute_OnActionExecuted_SetsConfigurationDctionaryInAppicationCache()
{
// Arrange
Mock<HttpContextBase> httpContext = new Mock<HttpContextBase>();
var mockApplicationState = new Mock<HttpApplicationStateBase>();
httpContext.Setup(h => h.Application).Returns(mockApplicationState.Object);
ApplicationController applicationController = new BlogController(null, null, MocksAndStubs.CreateMockApplicationService());
Mock<ActionExecutedContext> actionExecutedContext = new Mock<ActionExecutedContext>();
actionExecutedContext.SetupGet(c => c.HttpContext).Returns(httpContext.Object);
actionExecutedContext.SetupGet(c => c.Controller).Returns(applicationController);
// Act
ApplicationAttribute applicationAttribute = new ApplicationAttribute();
ActionExecutedContext context = actionExecutedContext.Object;
applicationAttribute.OnActionExecuted(context);
// Assert
Assert.IsNotNull(context.HttpContext.Application["config"]);
}
2 ответа
Похоже, вы ожидаете, что ваш фиктивный объект более или менее будет действовать как реальный объект. Это не работает таким образом; макет будет делать только то, что вы говорите, не больше, не меньше. Если вы не говорите макету возвращать что-то конкретное при звонке.,,
context.HttpContext.Application["config"]
,,, тогда это просто не будет. Если вы настроите макет так, чтобы он возвращал что-то конкретное, это помешало бы цели вашего теста.
Без какого-либо дополнительного понимания или вашей ситуации ("что" и "почему" в вашей тестовой ситуации) похоже, что вы пытаетесь гарантировать, что вызывается набор состояний приложения. Я бы порекомендовал сделать mockApplicationState.Verify()
как ваше утверждение, чтобы проверить, что набор произошел, вместо того, чтобы проверять результирующее состояние самого объекта.
Изменить: Verify () позволяет вам утверждать / гарантировать, что метод (или метод свойства) был вызван, с или без условий. Это должно помочь вам начать:
http://code.google.com/p/moq/wiki/QuickStart
Таким образом, ваша проверка будет выглядеть примерно так (полностью не проверено!):
mockApplicationState.Verify(x => x["config"] == [expected value], Times.Once());
Это в основном говорит о том, что провалить тест, если mockApplicationState["config"] было установлено на ожидаемое значение менее одного раза или более одного раза.
Я предполагаю, что HttpApplicationStateBase не запечатан. Если это так, то выше может выдать исключение.
В качестве альтернативы MOQ я часто в этом сценарии генерирую некоторые заглушки, полученные из базовых классов в System.Web.Abstractions. Я часто использую эту технику для приложений MVC, поскольку контроллеры MVC / WebApi содержат абстракцию к HttpContext (HttpContextBase)
Таким образом, я могу заглушить требования HttpContext в своих модульных / интеграционных тестах, вот пример...
public class MockHttpApplicationState : HttpApplicationStateBase
{
private IDictionary<string, object> _appState = new Dictionary<string, object>();
public override void Add(string name, object value)
{
_appState.Add(name, value);
}
public override object Get(string name)
{
return _appState[name];
}
public override object this[string name]
{
get
{
return _appState[name];
}
set
{
_appState[name] = value;
}
}
}
public class MockHttpContext : HttpContextBase
{
private IDictionary<string, object> _appKeys;
public MockHttpContext()
{
}
/// <summary>
/// Accepts a dictionary of app keys to supply to the HttpApplicationState instance
/// </summary>
/// <param name="applicationState"></param>
public MockHttpContext(IDictionary<string,object> applicationState)
{
_appKeys = applicationState;
}
public override Cache Cache
{
get
{
return HttpRuntime.Cache;
}
}
public override HttpApplicationStateBase Application
{
get
{
var mockAppState = new MockHttpApplicationState();
foreach (string key in _appKeys.Keys)
{
mockAppState.Add(key, _appKeys[key]);
}
return mockAppState;
}
}
public override HttpRequestBase Request
{
get
{
return new HttpRequestWrapper(new HttpRequest(null,"http://localhost",null));
}
}
}
Тогда мой тест может установить Контроллер и Http Context:
private readonly OnlineShop.MVC.Controllers.HomeController _controller =
new MVC.Controllers.HomeController(null,new UnitOfWork());
[OneTimeSetUp]
public void Init()
{
var appKeys = new Dictionary<string, object>();
appKeys.Add("localhost", 1);
var httpContext = new MockHttpContext(appKeys);
_controller.ControllerContext = new ControllerContext()
{
Controller = _controller,
RequestContext = new RequestContext(httpContext, new RouteData())
};
}
[Test]
public void Index_Returns_HomeView()
{
var view = _controller.Index() as ViewResult;
var viewModel = view.Model as MVC.ViewModels.Home;
Assert.IsInstanceOf<OnlineShop.MVC.ViewModels.Home>(viewModel);
Assert.IsTrue(viewModel.FeaturedProducts.Count > 0);
}
И мой контроллер знает, что это окружающий экземпляр HttpContextBase, предоставляющий состояние Cache и Application:
public ActionResult Index()
{
string cacheKey = string.Format("FeaturedProducts-{0}",WebsiteId);
IList<Product> productList = this.HttpContext.Cache[cacheKey] as IList<Product>;
//My app keeps a list of website contexts in the Application. This test returns 1 based on the unit / int tests or a real world db value when hosted on IIS etc..
int websiteId = (int)HttpContext.Application[this.Request.Url.Host];