【问题标题】:mock HttpContext.Current.Server.MapPath using Moq?使用 Moq 模拟 HttpContext.Current.Server.MapPath?
【发布时间】:2011-03-21 12:32:56
【问题描述】:

我对我的家庭控制器进行单元测试。在我添加了保存图像的新功能之前,此测试运行良好。

导致问题的方法如下。

    public static void SaveStarCarCAPImage(int capID)
    {
        byte[] capBinary = Motorpoint2011Data.RetrieveCapImageData(capID);

        if (capBinary != null)
        {
            MemoryStream ioStream = new MemoryStream();
            ioStream = new MemoryStream(capBinary);

            // save the memory stream as an image
            // Read in the data but do not close, before using the stream.

            using (Stream originalBinaryDataStream = ioStream)
            {
                var path = HttpContext.Current.Server.MapPath("/StarVehiclesImages");
                path = System.IO.Path.Combine(path, capID + ".jpg");
                Image image = Image.FromStream(originalBinaryDataStream);
                Image resize = image.GetThumbnailImage(500, 375, null, new IntPtr());
                resize.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
        }
    }

由于调用来自单元测试,HttpContext.Current 为 null 并引发异常。在阅读了有关 Moq 和一些关于在会话中使用 Moq 的教程后,我确信它可以完成。

到目前为止,单元测试代码已经提出了这个问题,但问题是 HTTPContext.Current 始终为空,并且仍然抛出异常。

    protected ControllerContext CreateStubControllerContext(Controller controller)
    {
        var httpContextStub = new Mock<HttpContextBase>
        {
            DefaultValue = DefaultValue.Mock
        };

        return new ControllerContext(httpContextStub.Object, new RouteData(), controller);
    }

    [TestMethod]
    public void Index()
    {
        // Arrange
        HomeController controller = new HomeController();            
        controller.SetFakeControllerContext();

        var context = controller.HttpContext;

        Mock.Get(context).Setup(s => s.Server.MapPath("/StarVehiclesImages")).Returns("My Path");

        // Act
        ViewResult result = controller.Index() as ViewResult;

        // Assert
        HomePageModel model = (HomePageModel)result.Model;
        Assert.AreEqual("Welcome to ASP.NET MVC!", model.Message);
        Assert.AreEqual(typeof(List<Vehicle>), model.VehicleMakes.GetType());
        Assert.IsTrue(model.VehicleMakes.Exists(x => x.Make.Trim().Equals("Ford", StringComparison.OrdinalIgnoreCase)));
    }

【问题讨论】:

    标签: c# unit-testing asp.net-mvc-2 moq


    【解决方案1】:

    HttpContext.Current 是绝对不应该使用的东西,如果你希望你的代码经过单元测试。它是一个静态方法,如果没有 Web 上下文(这是单元测试的情况并且不能被模拟),它只会返回 null。因此,重构代码的一种方法如下:

    public static void SaveStarCarCAPImage(int capID, string path)
    {
        byte[] capBinary = Motorpoint2011Data.RetrieveCapImageData(capID, path);
    
        if (capBinary != null)
        {
            MemoryStream ioStream = new MemoryStream();
            ioStream = new MemoryStream(capBinary);
    
            // save the memory stream as an image
            // Read in the data but do not close, before using the stream.
    
            using (Stream originalBinaryDataStream = ioStream)
            {
                path = System.IO.Path.Combine(path, capID + ".jpg");
                Image image = Image.FromStream(originalBinaryDataStream);
                Image resize = image.GetThumbnailImage(500, 375, null, new IntPtr());
                resize.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
        }
    }
    

    你看,现在这个方法不再依赖于任何网络环境,可以单独测试。调用者有责任传递正确的路径。

    【讨论】:

    • 好建议,但 Moq 似乎可以选择用“Mock.Get(context).Setup()”模拟在一起吗?
    • @Truegilly,避免使用HttpContext.Current。这只是一个好习惯。
    【解决方案2】:

    我同意达林的回答,但如果你真的需要最小起订量 Server.MapPath 函数,你可以做这样的事情

    //...
    var serverMock = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
    serverMock.Setup(i => i.MapPath(It.IsAny<String>()))
       .Returns((String a) => a.Replace("~/", @"C:\testserverdir\").Replace("/",@"\"));
    //...
    

    执行此操作,mock 将简单地将 ~/ 替换为 c:\testserverdir\ 函数

    希望对你有帮助!

    【讨论】:

    • 我错过了什么吗?我仍然无法模拟 HttpContext 的那一部分,因为 context.Server 没有设置器。所以我认为没有办法将你的 HttpServerUtilityBase 模拟与 HttpContext 一起使用
    【解决方案3】:

    有时模拟对 server.MapPath 的调用很方便。 这个解决方案适用于我使用最小起订量。 我只模拟应用程序的基本路径。

            _contextMock = new Mock<HttpContextBase>();
            _contextMock.Setup(x => x.Server.MapPath("~")).Returns(@"c:\yourPath\App");
            _controller = new YourController();
            _controller.ControllerContext = new ControllerContext(_contextMock.Object, new RouteData(), _controller);
    

    在您的控制器中,您现在可以使用 Server.MapPath("~")。

    【讨论】:

      【解决方案4】:

      以下对我有用。

      string pathToTestScripts = @"..\..\..\RelatavePathToTestScripts\";
      string testScriptsFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, pathToTestScripts);
      
      var server = new Mock<HttpServerUtilityBase>(); // Need to mock Server.MapPath() and give location of random.ps1
      server.Setup(x => x.MapPath(PowershellScripts.RANDOM_PATH)).Returns(testScriptsFolder + "random.ps1");
      
      var request = new Mock<HttpRequestBase>(); // To mock a query param of s=1 (which will make random.ps1 run for 1 second)
      request.Setup(x => x.QueryString).Returns(new System.Collections.Specialized.NameValueCollection { { "s", "1" } });
      
      var httpContext = new Mock<HttpContextBase>();
      httpContext.Setup(x => x.Server).Returns(server.Object);
      httpContext.Setup(x => x.Request).Returns(request.Object);
      
      YourController controller = new YourController();
      controller.ControllerContext = new ControllerContext(httpContext.Object, new RouteData(), controller);
      

      【讨论】:

        猜你喜欢
        • 2018-01-13
        • 2011-06-13
        • 2019-09-11
        • 2010-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多