Макет 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);