我正在构建一个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)在单元测试中,如何添加控制器需要的应用程序级变量?
答案 0 :(得分:1)
通常,全局变量不适合测试。你可以采取至少两种方法。
使用像Pex/Moles,NMock等模拟框架。
使用控制反转方法(NInject是我的最爱)。如果像控制器这样的类具有外部依赖关系,则它会询问接口,通常在其构造函数中。
private readonly IApplicationSettings _settings;
public MyController(IApplicationSettings设置) { _settings =设置; }
void someMethod() { _settings.Get( “MyVar的”); }
通过这种方式,您可以编写实际和测试实现。
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);
}