如何使用MOQ模拟ASP.NET MVC中的Request.ServerVariables?

11
我正在学习为我的ASP.NET MVC添加单元测试,当我了解到模拟和现在存在的不同框架时。
在查看SO之后,我发现MOQ似乎是最容易上手的。 目前,我正在尝试模拟Request.ServerVariables,因为在阅读了这篇文章之后,我了解到将它们抽象成属性会更好。
因此:
/// <summary>
        /// Return the server port
        /// </summary>
        protected string ServerPort
        {
            get
            {
                return Request.ServerVariables.Get("SERVER_PORT");
            }
        }

但我很难学习如何正确地模拟这个过程。我有一个主页控制器的ActionResult函数,它获取用户服务器信息并继续创建一个表单来获取用户的信息。
我尝试使用hanselman's mvcmockhelpers class,但我不确定如何使用它。
这是我目前的进展...
[Test]
        public void Create_Redirects_To_ProductAdded_On_Success() 
        {

            FakeViewEngine engine = new FakeViewEngine();

            HomeController controller = new HomeController();
            controller.ViewEngine = engine;

            MvcMockHelpers.SetFakeControllerContext(controller);

            controller.Create();

            var results = controller.Create();

            var typedResults = results as RedirectToRouteResult;

            Assert.AreEqual("", typedResults.RouteValues["action"], "Wrong action");
            Assert.AreEqual("", typedResults.RouteValues["controller"], "Wrong controller");
        }

问题:

  1. 目前我运行测试时仍然会得到null异常错误。那么我在这里缺少什么?
  2. 如果我使用mvcmockhelpers类,我如何调用request.verifyall函数以确保所有模拟都已正确设置?
2个回答

18

基本上,我已经放置了所有的参考资料,并使用这个函数使其正常工作。我还没有使用mvcmockhelpers类,因为我仍在努力学习。

对于任何有兴趣查看我是如何解决这个问题的人,这是我使用的代码。

[Test]
public void Create_Returns_ViewResult_On_Success() 
{
  var server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
  var response = new Mock<HttpResponseBase>(MockBehavior.Strict);

  var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
  request.Setup(x => x.ApplicationPath).Returns("/");
  request.Setup(x => x.Url).Returns(new Uri("http://localhost"));
  request.Setup(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection{
    { "SERVER_NAME", "localhost" },
    { "SCRIPT_NAME", "localhost" },
    { "SERVER_PORT", "80" },
    { "HTTPS", "www.melaos.com" },
    { "REMOTE_ADDR", "127.0.0.1" },
    { "REMOTE_HOST", "127.0.0.1" }
  });

  var context = new Mock<HttpContextBase>();
  context.SetupGet(x => x.Request).Returns(request.Object);
  context.SetupGet(x => x.Response).Returns(response.Object);
  context.SetupGet(x => x.Server).Returns(server.Object);

  var controller = new HomeController();
  //MvcMockHelpers.SetFakeControllerContext(controller);
  controller.ControllerContext = new ControllerContext(context. Object, new RouteData(), controller);

  var results = controller.Create();
  Assert.IsNotNull(results);
  Assert.IsInstanceOf(typeof(ViewResult), results);
}

0

由于我想模拟HttpContext,而不是HttpContextBase,因此我尝试找到一种将HttpContextBase模拟对象转换为HttpContext的方法,并发现这是不可能的。

HttpContext
这是经典的asp.net上下文。它的问题在于它没有基类,也不是虚拟的,因此无法用于测试(无法模拟)。建议不要将其作为函数参数传递,而是传递类型为HttpContextBase的变量。
HttpContextBase 这是(新的c# 3.5)替代HttpContext的对象。由于它是抽象的,现在可以进行模拟。想要接收到一个上下文的函数应该期望接收到这些对象之一。它由HttpContextWrapper具体实现。
HttpContextWrapper C# 3.5中的新功能 - 这是HttpContextBase的具体实现。要在普通网页中创建其中一个,请使用new HttpContextWrapper(HttpContext.Current)。
思路是为了使您的代码可单元测试,您应该声明所有变量和函数参数的类型为HttpContextBase,并使用IOC框架例如Castle Windsor进行注入。在正常代码中,castle将注入等效于“new HttpContextWrapper(HttpContext.Current)”的内容,而在测试代码中,您将获得HttpContextBase的模拟。
(来源:http://www.splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext/

所以我改变了调用我的代码的方式。

从使用 HttpContext

public static string GetIpAddress(HttpContext currentContext) {
 ...
}

使用 HttpContextBase

public static string GetIpAddress(HttpContextBase currentContext) {
 ...
}

当我使用这个方法时,我会像这样用HttpContextWrapper包装HttpContext

var ip = GeneralUtil.GetIpAddress(new HttpContextWrapper(HttpContext.Current));

现在你可以通过以下 Dan Atkinson的回复 轻松地模拟HttpContextBase


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接