【问题标题】:Automatic testing of ApiControllerApiController 的自动测试
【发布时间】:2016-11-04 13:24:44
【问题描述】:

我有一个ApiController,并希望通过包括路由在内的单元测试对其进行测试。

一个例子:

[RoutePrefix("prefix")]
public class Controller : ApiController
{
    [HttpGet]
    [Route("{id1}")]
    public int Add(int id1, [FromUri] int id2)
    {
        return id1 + id2;
    }
}

我现在想测试这个方法。我明白了,我可以像普通方法一样测试它。但我也想通过将 URL 转换为方法参数来测试它。

基本上我想要一个自动测试,我调用像prefix/10?id2=5 这样的 URL 并得到 15 的结果。这有可能吗?

【问题讨论】:

  • 您想要测试您的路线。想要测试是很常见的事情,有几个关于如何做的指南。
  • 在 Web API 中研究 TestServer。这将被视为集成测试。

标签: c# unit-testing testing asp.net-web-api


【解决方案1】:

我为内存集成测试编写了一个小助手类,可以作为测试套件的一部分调用。

internal interface IHttpTestServer : IDisposable {
    HttpConfiguration Configuration { get; }
    HttpClient CreateClient();
}

internal class HttpTestServer : IHttpTestServer {
    HttpServer httpServer;

    public HttpTestServer(HttpConfiguration configuration = null) {
        httpServer = new HttpServer(configuration ?? new HttpConfiguration());
    }

    public HttpConfiguration Configuration {
        get { return httpServer.Configuration; }
    }

    public HttpClient CreateClient() {
        var client = new HttpClient(httpServer);
        return client;
    }

    public void Dispose() {
        if (httpServer != null) {
            httpServer.Dispose();
            httpServer = null;
        }
    }

    public static IHttpTestServer Create(HttpConfiguration configuration = null) {
        return new HttpTestServer(configuration);
    }
}

然后会像这样使用它

[TestMethod]
public async Task HttpClient_Should_Get_OKStatus_From_InMemory_Hosting() {

    using (var server = new HttpTestServer()) {

        MyWebAPiProjectNamespace.WebApiConfig.Configure(server.Configuration);

        var client = server.CreateClient();

        string url = "http://localhost/prefix/10?id2=5";
        var expected = 15;

        var request = new HttpRequestMessage {
            RequestUri = new Uri(url),
            Method = HttpMethod.Get
        };

        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        using (var response = await client.SendAsync(request)) {
            Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            var result = await response.Content.ReadAsAsync<int>();
            Assert.AreEqual(expected, result);
        }
    }
}

这将配置一个内存测试服务器,测试可以使用它的 httpclient 调用它。它本质上是一个端到端的集成测试。

【讨论】:

  • 我看不到,这与我的Controller 类有关。
  • 如果您查看 url,您会发现它正在尝试测试控制器中的路由以及响应内容。这是您在问题中想要的。我弄错了吗?
  • 我想你有我正在寻找的答案。我很难让它运行。我找不到 WebApiConfig。还有 IntegrationTest 属性...我想我不需要它?
  • 你不需要它。这是我在测试项目中的自定义属性。将更新答案。至于 WebApiConfig。在您的 web api 项目中查找 Startup 文件夹。它应该在那里。只要确保你在测试中有正确的命名空间。
  • 我没看到。有一个静态的Startup 类,有一个方法ConfigureApp()。没有文件夹Startup。整个项目是一个 azure service fabric web api——如果重要的话。我无法让MyWebAPiProjectNamespace.WebApiConfig.Configure(server.Configuration); 工作。我已经尝试了 Controller 类的命名空间和 Startup 类的命名空间。
【解决方案2】:
  1. 使用 Microsoft ASP.NET Web API 2.2 OWIN 包创建 OWIN StartUp 类:

    public class Startup
    { 
        public void Configuration(IAppBuilder builder)
        {
            var config = new HttpConfiguration();
    
            builder.UseWebApi(config);
    
            config.MapHttpAttributeRoutes();
            config.EnsureInitialized();
        }
    }
    
  2. 在您的测试中使用 Microsoft ASP.NET Web API 2.2 Self Host 包(例如使用 NUnit):

    [Test]
    [TestCase(10, 5, 15)]
    [TestCase(1, 2, 3)]
    // add your test cases
    public async Task AdditionTests(int a, int b, int result) 
    {
        // Arrange
        var address = "http://localhost:5050";
    
        using (WebApp.Start<Startup>(address))
        {
            var client = new HttpClient();
    
            var requestUri = $"{address}/prefix/{a}?id2={b}";
    
            // Act
            var response = await client.GetAsync(requestUri);
    
            // Assert
            Assert.IsTrue(await response.Content.ReadAsAsync<int>() == result);
        }
    }
    

【讨论】:

    【解决方案3】:

    这是一个集成测试,而不是单元测试。如果你想自动化这个,你必须有一个工具来启动/托管你的 web api,然后针对它执行请求。

    如果您想将其保留为单元测试,尽管您可以验证类和方法的属性并检查值。

    var type = typeof(Controller);
    var attributeRoutePrefix = type.GetCustomAttribute(typeof(RoutePrefixAttribute)) as RoutePrefixAttribute;
    Assert.IsNotNull(attributeRoutePrefix);
    Assert.AreEqual("prefix", attributeRoutePrefix.Prefix);
    
    var methodAttribute = type.GetMethod(nameof(Controller.Add)).GetCustomAttribute(typeof(RouteAttribute)) as RouteAttribute;
    Assert.IsNotNull(methodAttribute);
    Assert.AreEqual("id1", methodAttribute.Template);
    

    【讨论】:

      【解决方案4】:

      可以使用 postman 或 fiddler 来测试 url 参数..

      【讨论】:

      • @stuartd,当您开始使用 URL 进行测试时,您就不再是单元测试了。它被称为功能/验收/集成测试(取决于您来自何时),并且您不限于单一语言。老实说,我不理解反对票,因为这是通过外部工具进行的合法测试方法。
      • 单元测试是不可能的,但是测试方法的另一种选择。
      • @stuartd 在投反对票之前,请考虑如何在没有外部工具的情况下在单元测试中测试 url。
      • 好吧,你可以@Satheshkumar .. 你只需要一个框架或其他东西,它可以按需创建请求并让你能够评估结果。 PHP 有代码接收(例如),但我很确定 C# 也有类似的东西。但是,是的,使用 postman/filder/... 也是一种非常有效的方法。
      • @walther 够公平的。但我想说问题不在于如何测试 URL,而在于如何对定义的路由进行单元测试,即certianly possible without external tools
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-20
      • 1970-01-01
      • 2011-12-17
      • 1970-01-01
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多