ASP.NET MVC: контроллеры модульного тестирования, использующие UrlHelper

170

Одно из моих действий контроллеров, которое вызывается в Ajax-запросе, возвращает URL-адрес клиентской стороне, чтобы он мог выполнить перенаправление. Я использую Url.RouteUrl(..)и во время моих модульных тестов это не удается, так как Controller.Urlпараметр не заполнен предварительно.

Я пробовал много вещей, среди других , пытающегося шлейфа UrlHelper(который не удался), создавая вручную UrlHelperс RequestContextкоторый имеет погасил HttpContextBase(который не удался на RouteCollection.GetUrlWithApplicationPathвызов).

Я искал Google, но практически ничего не нашел по этому вопросу. Я делаю что-то невероятно глупое, используя Url.RouteUrlв своем действии Контроллер? Есть ли более простой способ?

Что еще хуже, я хотел бы иметь возможность проверить возвращенный URL в моем модульном тесте - на самом деле меня интересует только то, что он перенаправляет на правильный маршрут, но так как я возвращаю URL вместо В маршруте я хотел бы контролировать разрешенный URL-адрес (например, с помощью заглушки RouteCollection), но я буду рад начать тестирование.

efdee
источник

Ответы:

202

Вот один из моих тестов (xUnit + Moq) только для аналогичного случая (с использованием Url.RouteUrl в контроллере)

Надеюсь это поможет:

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);

var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());

var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");

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

var controller = new LinkbackController(dbF.Object);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
ес-г-п
источник
2
В настоящее время я выбрал решение, в котором абстрагировал вызовы UrlHelper, чтобы их можно было перехватить. Спасибо за ваш фрагмент, однако, это сэкономит мне много времени, выясняя, как правильно смоделировать Request / Response / ControllerContext.
efdee
Спасибо за ответ @ eu-ge-ne, он мне тоже очень помог. Я включил еще несколько настроек moq для использования параметра formcollection, используемого UpdateModel
jebcrum
16
+1 отлично. Хотя совет: я использую это как MockHelper и меняю response.Setup для ApplyAppPathModifier на это: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ())). Returns ((String url) ) => URL); Это некрасиво, но я возвращаю сериализованный объект обратно в кодированную форму url, вместо того чтобы жестко кодировать возвращаемое значение
eduncan911
Это частично работает для меня. Любые идеи, почему я получаю контроллер / вместо контроллера / действия? Мой тест не пройден, потому что они не совсем одинаковые, и все же я регистрирую одинаковые значения маршрутизации. Очень странно ...
Ник
3
Эта ApplyAppPathModifierчасть является критической для UrlHelper
Крис С
37

Модифицированная реализация от eu-ge-ne. Этот возвращает сгенерированную ссылку на основе маршрутов, определенных в приложении. Пример eu-ge-ne всегда возвращал фиксированный ответ. Приведенный ниже подход позволит вам проверить, что правильное действие / контроллер и информация о маршруте передаются в UrlHelper - это то, что вам нужно, если вы тестируете вызов UrlHelper.

var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();

context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);

request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());

response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);

context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);

var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var helper = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
Стивен Пена
источник
12

Этот пост может быть полезен, если вы хотите издеваться над классом HttpContextBase.

http://www.hanselman.com/blog/ASPNETMVCSessionAtMix08TDDAndMvcMockHelpers.aspx

Херардо Контиох
источник
Круто, это помогло мне, хотя мне пришлось добавить некоторый дополнительный код в метод FakeHttpContext, чтобы остановить взрыв помощника: context.Setup (ctx => ctx.Request.ApplicationPath) .Returns ("/ AntiBlowup"); Я также реорганизовал код, чтобы он использовал новый синтаксис Setup (). Спасибо.
RichardOD
2

Построение ответа @ eu-ge-ne, который мне очень помог:

У меня был ActionResult, который выполнял перенаправление, а также имел вызов UpdateModel с параметром FormCollection. Чтобы UpdateModel () работал, мне нужно было добавить это в мой Mocked HttpRequestBase:

FormCollection collection = new FormCollection();
collection["KeyName"] = "KeyValue";

request.Setup(x => x.Form).Returns(collection);
request.Setup(x => x.QueryString).Returns(new NameValueCollection());

Чтобы проверить правильность перенаправленного URL, вы можете сделать следующее:

RedirectResult result = controller.ActionName(modelToSubmit, collection) as RedirectResult;
Assert.AreEqual("/Expected/URL", result.Url);
jebcrum
источник