2

我有一个动作过滤器,它在过滤器上下文的 HttpContext 的 HttpApplicationState 中设置一个对象。我希望在单元测试中具有此功能,但由于某种原因,该对象未在 HttpApplicationState 派生的基础 NameObjectCollectionBase 中设置。

我知道该功能有效,因为当我运行 MVC 应用程序时,它按预期工作。

如何设置我的测试以使其在应用程序状态下设置对象?我使用的是起订量,这里是到目前为止的一些代码。它失败了

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"]);
}
4

2 回答 2

2

看起来您希望您的模拟对象或多或少像真实对象一样。它不是那样工作的;模拟只会做你告诉它做的事情,不多也不少。如果您在调用 . . .

context.HttpContext.Application["config"]

. . . 那么它根本不会。如果您确实将模型设置为返回特定的内容,那么它将违背您的测试目的。

如果没有任何额外的理解或您的情况(您的测试策略的“什么”和“为什么”),看起来您正在尝试确保调用应用程序状态的集合。我建议 mockApplicationState.Verify()你做一个断言来测试集合发生了,而不是测试对象本身的结果状态。

编辑: Verify() 允许您断言/确保调用了方法(或属性方法),无论是否有条件。这应该让你开始:

http://code.google.com/p/moq/wiki/QuickStart#Verification

所以你的验证看起来像(完全未经测试!):

mockApplicationState.Verify(x => x["config"] == [expected value], Times.Once());

这基本上表示如果 mockApplicationState["config"] 设置为少于一次或多于一次的预期值,则测试失败。

我假设 HttpApplicationStateBase 没有密封。如果是,那么上面可能会引发异常。

于 2011-08-29T17:10:06.383 回答
0

作为 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));
        }
    }
}

然后我的测试可以建立Controller和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 实例提供缓存和应用程序状态:

  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];
于 2016-09-06T13:46:42.410 回答