У меня есть следующее действие контроллера
public void Post(Dto model)
{
using (var message = new MailMessage())
{
var link = Url.Link("ConfirmAccount", new { model.Id });
message.To.Add(model.ToAddress);
message.IsBodyHtml = true;
message.Body = string.Format(@"<p>Click <a href=""{0}"">here</a> to complete your registration.<p><p>You may also copy and paste this link into your browser.</p><p>{0}</p>", link);
MailClient.Send(message);
}
}
Чтобы проверить это, мне нужно настроить контекст контроллера
var httpConfiguration = new HttpConfiguration(new HttpRouteCollection { { "ConfirmAccount", new HttpRoute() } });
var httpRouteData = new HttpRouteData(httpConfiguration.Routes.First());
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost");
sut = new TheController
{
ControllerContext = new HttpControllerContext(httpConfiguration, httpRouteData, httpRequestMessage),
MailClient = new SmtpClient { PickupDirectoryLocation = location }
};
Это похоже на множество настроек для проверки создания ссылки. Есть ли более чистый способ сделать это? Я читал о серверах на памяти, но похоже, что он больше подходит для httpclient, чем непосредственно для тестирования контроллера.
Ответ 1
Ниже приведен абсолютный минимальный код, необходимый для тестирования UrlHelper
без какой-либо насмешливой библиотеки. То, что меня бросило (и мне потребовалось некоторое время, чтобы выследить), было то, что вам нужно установить IHttpRouteData
запроса. Если вы не используете экземпляр IHttpRoute
, вы не сможете создать виртуальный путь, в результате получивший пустой URL.
public class FooController : ApiController
{
public string Get()
{
return Url.Link(RouteNames.DefaultRoute, new { controller = "foo", id = "10" });
}
}
[TestFixture]
public class FooControllerTests
{
FooController controller;
[SetUp]
public void SetUp()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost");
request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = new HttpRouteData(new HttpRoute());
controller = new FooController
{
Request = request
};
}
[Test]
public void Get_returns_link()
{
Assert.That(controller.Get(), Is.EqualTo("http://localhost/api/foo/10"));
}
}
Ответ 2
Я начал использовать этот подход с помощью Web API 2.0.
Если вы используете насмешливую библиотеку (и вы действительно должны это делать для реальных тестов в реальном мире), вы можете напрямую издеваться над объектом UrlHelper
, так как все методы на нем virtual
.
var mock = new Mock<UrlHelper>();
mock.Setup(m => m.Link(It.IsAny<string>(), It.IsAny<object>())).Returns("test url");
var controller = new FooController {
Url = mock.Object
};
Это намного более чистое решение, чем ответ Ben Foster, так как с этим подходом вам нужно добавить маршруты в конфигурацию для каждого имени, которое вы используете. Это может легко измениться или быть смехотворно большим количеством маршрутов для настройки.
Ответ 3
Я сталкиваюсь с тем же идиотизмом. Все ссылки, которые я могу найти, хотят, чтобы вы Mock Request/Controller, который (как вы указали) много работал.
Конкретные ссылки:
Я не собираюсь разбираться в реальных средах Mocking, поэтому у меня есть вспомогательный класс для "сборки" моего контроллера. Поэтому вместо
sut = new TheController { ... }
Я использую что-то вроде:
// actually rolled together to `sut = MyTestSetup.GetController(method, url)`
sut = new TheController()...
MyTestSetup.FakeRequest(sut, HttpMethod.Whatever, "~/the/expected/url");
Для справки, метод в основном:
public void FakeRequest(ApiController controller, HttpMethod method = null, string requestUrl = null, string controllerName = null) {
HttpConfiguration config = new HttpConfiguration();
// rebuild the expected request
var request = new HttpRequestMessage( null == method ? this.requestMethod : method, string.IsNullOrWhiteSpace(requestUrl) ? this.requestUrl : requestUrl);
//var route = System.Web.Routing.RouteTable.Routes["DefaultApi"];
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
// TODO: get from application? maybe like https://stackoverflow.com/a/5943810/1037948
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", string.IsNullOrWhiteSpace(controllerName) ? this.requestController : controllerName } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
// attach fake request
controller.Request = request;
controller.Request.Properties[/* "MS_HttpConfiguration" */ HttpPropertyKeys.HttpConfigurationKey] = config;
}