ASP.NET MVC: контролери тестування блоків, які використовують UrlHelper


170

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

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

Я шукав Google, але практично нічого не знайшов на цю тему. Чи роблю я щось неймовірно дурне, використовуючи Url.RouteUrlв моїй дії Controller? Чи є простіший спосіб?

Щоб зробити це ще гірше, я хотів би мати можливість перевірити повернуту URL-адресу в моєму тесті на одиницю - насправді мені цікаво лише знати, що вона перенаправляється на правильний маршрут, але оскільки я повертаю URL-адресу замість Маршрут, я хотів би контролювати розв’язану URL-адресу (наприклад, за допомогою стертих RouteCollection), але я буду радий розпочати тест.

Відповіді:


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, щоб я міг їх перехопити. Дякуємо за ваш фрагмент, однак це дозволить мені зекономити багато часу, з'ясовуючи, як правильно знущатися із запиту / відповіді / ControllerContext.
efdee

Дякую за відповідь @ eu-ge-ne, і мені це дуже допомогло. Я включив ще кілька параметрів moq, щоб використовувати параметр
formcollection,

16
+1 відмінно. Хоча підказка: я використовую це як MockHelper і змінюю відповідь. Налаштування для ApplyAppPathModifier на це: response.Setup (x => x.ApplyAppPathModifier (Moq.It.IsAny <String> ())) Повернення ((String url ) => URL); Це некрасиво, але я повертаю серіалізований об’єкт назад у кодованому URL-адресі замість жорсткого кодування поверненого значення.
eduncan911

Це частково працює для мене. Будь-які ідеї, чому я отримую Controller / замість Controller / Action? Мій тест не вдається, оскільки вони не зовсім однакові, і все ж я реєструю однакові значення маршрутизації. Дуже дивно ...
Нік

3
Ця ApplyAppPathModifierчастина є критичним для UrlHelper
Chris S

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.