使用应用程序作用域变量的单元测试控制器

时间:2012-08-19 01:25:18

标签: asp.net asp.net-mvc unit-testing controller

我正在构建一个ASP.NET MVC4应用程序。我没有使用任何模拟框架,如果可能的话,我不想在这一点上。我的问题是2部分。

我有一个控制器,它使用在Global.asax中创建的变量。在控制器中,我像这样访问变量。

HttpContext.Application["MyVar"]

1)这是应用程序范围变量使用的最佳实践吗?如果没有,最好的方法是什么?

在尝试对此控制器进行单元测试时,我将以下代码(从here)添加到我的测试方法中。

MyController target = new MyController();
var request = new HttpRequest("", "http://example.com/", "");
var response = new HttpResponse(System.IO.TextWriter.Null);
var httpContext = new HttpContextWrapper(new HttpContext(request, response));
target.ControllerContext = new ControllerContext(httpContext, new RouteData(), target);
target.ControllerContext.HttpContext.Application["MyVar"] = new MyVar();

问题是我无法向Application添加任何内容。最后一行代码似乎没有做任何事情,集合仍然是空的。我也在VS的立即窗口中试过这个但没有成功。

2)在单元测试中,如何添加控制器需要的应用程序级变量?

2 个答案:

答案 0 :(得分:1)

通常,全局变量不适合测试。你可以采取至少两种方法。

  1. 使用像Pex/Moles,NMock等模拟框架。

  2. 使用控制反转方法(NInject是我的最爱)。如果像控制器这样的类具有外部依赖关系,则它会询问接口,通常在其构造函数中。

    private readonly IApplicationSettings _settings;

    public MyController(IApplicationSettings设置) {     _settings =设置; }

    void someMethod() {     _settings.Get( “MyVar的”); }

  3. 通过这种方式,您可以编写实际和测试实现。

    public LiveAppSettings : IApplicationSettings
    {
        public string Get(string key)
        { 
            return HttpContext.Current.Application[key];
        }
    }
    

    使用Ninject,您可以在应用程序启动时绑定任一实现:

    var kernel = new StandardKernel();
    kernel.Bind<IApplicationSettings>().To<LiveAppSettings>();
    

答案 1 :(得分:0)

  

这是应用程序范围变量使用的最佳实践吗?

最佳做法是一种主观概念,如果没有完全解释你的情景,你究竟想要实现什么,我宁愿不去讨论它。

我们无法讨论这是否是最佳做法,但从我可以看出它也没有错。这没有错,因为您正在使用抽象,允许对代码进行单元测试。

  

在单元测试中,如何添加控制器所需的应用程序级变量?

您可以使用模拟框架(如Rhino Mocks)来模拟控制器所需的抽象。我们以下面的控制器为例:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var myVar = (MyVar)HttpContext.Application["MyVar"];
        return Content(myVar.Foo);
    }
}

我们希望对Index操作进行单元测试。这是一个样本单元测试:

[TestMethod]
public void Index_Action_Should_Retrieve_MyVal_From_AppState()
{
    // arrange
    var target = new HomeController();
    var httpContext = MockRepository.GeneratePartialMock<HttpContextBase>();
    var application = MockRepository.GeneratePartialMock<HttpApplicationStateBase>();
    application.Expect(x => x["MyVar"]).Return(new MyVar { Foo = "bar" });
    httpContext.Expect(x => x.Application).Return(application);
    target.ControllerContext = new ControllerContext(httpContext, new RouteData(), target);

    // act
    var actual = target.Index() as ContentResult;

    // assert
    Assert.AreEqual("bar", actual.Content);
}