Testing WebApi Controller Url.Link

Viewed 8254

I have the following controller action

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

To test this I need to setup the controller context

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

This seems like a lot of setup to test the creation of a link. Is there a cleaner way to do this? I have read about in-memory servers but that looks like it applies more to the httpclient than testing the controller directly.

3 Answers

Below is the absolute minimum code required to test UrlHelper without any kind of mocking library. The thing that threw me (and took me some time to track down) was that you need to set the IHttpRouteData of the request. If you don't the IHttpRoute instance will fail to generate a virtual path resulting in an empty 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"));
        }
    }
Related