Макет HttpContext.Current.Server.MapPath, используя Moq?

Я тестирую мой домашний контроллер. Этот тест работал нормально, пока я не добавил новую функцию, которая сохраняет изображения.

Метод, который вызывает проблему, это ниже.

    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)));
    }

3 ответа

Решение

HttpContext.Current это то, что вам никогда не следует использовать, если вы ожидаете, что ваш код будет подвергнут модульному тестированию. Это статический метод, который просто возвращает ноль, если нет веб-контекста, который является случаем модульного теста и не может быть смоделирован. Таким образом, один из способов реорганизации вашего кода будет следующим:

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);
        }
    }
}

Видите ли, теперь этот метод больше не зависит от какого-либо веб-контекста и может тестироваться изолированно. Вызывающий абонент должен будет пройти правильный путь.

Я согласен с ответом Дарина, но если вам действительно нужно использовать функцию 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\

Надеюсь, поможет!

Иногда удобно просто посмеяться над вызовом server.MapPath. Это решение работает для меня, используя moq. Я только издеваюсь над базовым путем к приложению.

        _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("~").

Ниже работает у меня.

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);
Другие вопросы по тегам