11

我有以下控制器动作

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,而不是直接测试控制器。

4

3 回答 3

16

我开始在 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 的答案更干净的解决方案,因为使用这种方法,您需要为您使用的每个名称添加路由到配置。这很容易改变或设置大量可笑的路线。

于 2015-09-16T14:07:16.310 回答
12

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"));
        }
    }
于 2013-11-23T22:03:07.377 回答
2

我遇到了同样的白痴。我能找到的所有参考资料都希望您模拟请求/控制器,这(正如您所指出的)需要做很多工作。

具体参考:

我还没有尝试实际的 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;
}
于 2012-08-20T19:52:53.447 回答