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

时间:2010-05-05 02:29:21

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

当我开始学习模拟和现在的不同框架时,我正在学习为我的asp.net mvc进行单元测试。

在检查了SO之后,我发现MOQ似乎是最容易接受的。 截至目前,我一直试图模拟Request.ServerVariables,如after reading this post,我已经了解到将它们抽象为属性更好。

因此:

/// <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. 截至目前,我仍然无效 我运行时出现异常错误 测试。那么我在这里缺少什么?
  2. 如果我使用mvcmockhelpers 上课,我怎么还能打电话给 request.verifyall功能确保 所有的嘲笑都正确设置了吗?

2 个答案:

答案 0 :(得分:16)

所以基本上我已经把所有的参考文献和使用这个功能我能够让它工作。我还没有使用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);
}

答案 1 :(得分: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来注入它。正常   代码,城堡是注入相当于'新的   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's reply轻松模拟 HttpContextBase