zoukankan      html  css  js  c++  java
  • 为AspNetCore中间件编写单元测试

    1.编辑单元测试项目文件
       例如使用xUnitTest项目时,在项目文件中的Project节点下加入如下代码:

    1     <ItemGroup>
    2         <FrameworkReference Include="Microsoft.AspNetCore.App"></FrameworkReference>
    3     </ItemGroup>

    2.配置和构建Host

       例如配置服务、指定要使用的服务器以及加入需要测试的中间件,主要代码如下:

     1     using IHost host = new HostBuilder()
     2     .ConfigureWebHost(webHostBuilder =>
     3         {
     4             webHostBuilder
     5             .ConfigureServices(services =>
     6             {
     7                 services.AddHttpContextAccessor();
     8                 services.AddLogging();
     9                 services.AddRouting();
    10             })
    11             .UseKestrel()
    12             .UseUrls(GetTestUrl(server))
    13             .UseWebRoot(AppContext.BaseDirectory)
    14             .Configure(app =>
    15             {
    16                 app.UseRouting();
    17 
    18                 app.Use(next => context =>
    19                 {
    20                     // Assign an endpoint, this will make the default files noop.
    21                     context.SetEndpoint(new Endpoint((c) =>
    22                         {
    23                             return context.Response.WriteAsync("endpoint.");
    24                         },
    25                         new EndpointMetadataCollection(), "test"));
    26 
    27                     return next(context);
    28                 });
    29 
    30                 app.UseStaticFiles();
    31 
    32                 //这里是需要单测的中间件
    33                 app.UseDemoMiddleware();
    34 
    35                 app.UseEndpoints(endpoints => { });
    36             });
    37         })
    38     .Build();

    3.启动和使用Host
       配置完Host后,将该Host实例启动,并且使用HttpClient模拟请求,以期执行自定义的中间件,主要代码如下:

    1     await host.StartAsync();
    2 
    3     using var client = new HttpClient { BaseAddress = new Uri(GetAddress(host)) };
    4     string url = "/api/demo";
    5     var response = await client.GetAsync(url);
    6     
    7     Assert.Equal(HttpStatusCode.OK, response.StatusCode);

    4.完整示例如下
       
    4.1 ServerType

    1     public enum ServerType
    2     {
    3         None,
    4         IISExpress,
    5         IIS,
    6         HttpSys,
    7         Kestrel,
    8         Nginx
    9     }

       4.2 TestUriHelper

     1     public static class TestUriHelper
     2     {
     3         public static Uri BuildTestUri(ServerType serverType)
     4         {
     5             return BuildTestUri(serverType, hint: null);
     6         }
     7 
     8         public static Uri BuildTestUri(ServerType serverType, string hint)
     9         {
    10             // Assume status messages are enabled for Kestrel and disabled for all other servers.
    11             var statusMessagesEnabled = (serverType == ServerType.Kestrel);
    12 
    13             return BuildTestUri(serverType, Uri.UriSchemeHttp, hint, statusMessagesEnabled);
    14         }
    15 
    16         internal static Uri BuildTestUri(ServerType serverType, string scheme, string hint, bool statusMessagesEnabled)
    17         {
    18             if (string.IsNullOrEmpty(hint))
    19             {
    20                 if (serverType == ServerType.Kestrel && statusMessagesEnabled)
    21                 {
    22                     // Most functional tests use this codepath and should directly bind to dynamic port "0" and scrape
    23                     // the assigned port from the status message, which should be 100% reliable since the port is bound
    24                     // once and never released.  Binding to dynamic port "0" on "localhost" (both IPv4 and IPv6) is not
    25                     // supported, so the port is only bound on "127.0.0.1" (IPv4).  If a test explicitly requires IPv6,
    26                     // it should provide a hint URL with "localhost" (IPv4 and IPv6) or "[::1]" (IPv6-only).
    27                     return new UriBuilder(scheme, "127.0.0.1", 0).Uri;
    28                 }
    29                 else if (serverType == ServerType.HttpSys)
    30                 {
    31                     Debug.Assert(scheme == "http", "Https not supported");
    32                     return new UriBuilder(scheme, "localhost", 0).Uri;
    33                 }
    34                 else
    35                 {
    36                     // If the server type is not Kestrel, or status messages are disabled, there is no status message
    37                     // from which to scrape the assigned port, so the less reliable GetNextPort() must be used.  The
    38                     // port is bound on "localhost" (both IPv4 and IPv6), since this is supported when using a specific
    39                     // (non-zero) port.
    40                     return new UriBuilder(scheme, "localhost", GetNextPort()).Uri;
    41                 }
    42             }
    43             else
    44             {
    45                 var uriHint = new Uri(hint);
    46                 if (uriHint.Port == 0)
    47                 {
    48                     // Only a few tests use this codepath, so it's fine to use the less reliable GetNextPort() for simplicity.
    49                     // The tests using this codepath will be reviewed to see if they can be changed to directly bind to dynamic
    50                     // port "0" on "127.0.0.1" and scrape the assigned port from the status message (the default codepath).
    51                     return new UriBuilder(uriHint) { Port = GetNextPort() }.Uri;
    52                 }
    53                 else
    54                 {
    55                     // If the hint contains a specific port, return it unchanged.
    56                     return uriHint;
    57                 }
    58             }
    59         }
    60 
    61         private static int GetNextPort()
    62         {
    63             using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    64             socket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
    65             return ((IPEndPoint)socket.LocalEndPoint).Port;
    66         }
    67     }

       4.3 DemoMiddlewareUnitTest

     1     public class DemoMiddlewareUnitTest
     2     {
     3         [Fact]
     4         public async Task DemoMiddlewareTest()
     5         {
     6             await DemoMiddleware(ServerType.IIS);
     7             await DemoMiddleware(ServerType.IISExpress);
     8             await DemoMiddleware(ServerType.Kestrel);
     9             await DemoMiddleware(ServerType.Nginx);
    10         }
    11 
    12         private async Task DemoMiddleware(ServerType server)
    13         {
    14             using IHost host = new HostBuilder()
    15                 .ConfigureWebHost(webHostBuilder =>
    16                     {
    17                         webHostBuilder
    18                         .ConfigureServices(services =>
    19                         {
    20                             services.AddHttpContextAccessor();
    21                             services.AddLogging();
    22                             services.AddRouting();
    23                         })
    24                         .UseKestrel()
    25                         .UseUrls(GetTestUrl(server))
    26                         .UseWebRoot(AppContext.BaseDirectory)
    27                         .Configure(app =>
    28                         {
    29                             app.UseRouting();
    30 
    31                             app.Use(next => context =>
    32                             {
    33                                 // Assign an endpoint, this will make the default files noop.
    34                                 context.SetEndpoint(new Endpoint((c) =>
    35                                     {
    36                                         return context.Response.WriteAsync("endpoint.");
    37                                     },
    38                                     new EndpointMetadataCollection(), "test"));
    39 
    40                                 return next(context);
    41                             });
    42 
    43                             app.UseStaticFiles();
    44 
    45                             app.UseDemoMiddleware();
    46 
    47                             app.UseEndpoints(endpoints => { });
    48                         });
    49                     })
    50                 .Build();
    51 
    52             await host.StartAsync();
    53 
    54             using var client = new HttpClient { BaseAddress = new Uri(GetAddress(host)) };
    55             string url = "/api/demo";
    56             var response = await client.GetAsync(url);
    57 
    58             Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    59             Assert.Equal("endpoint.", await response.Content.ReadAsStringAsync());
    60         }
    61 
    62         private string GetTestUrl(ServerType serverType)
    63         {
    64             return TestUriHelper.BuildTestUri(serverType).ToString();
    65         }
    66 
    67         private string GetAddress(IHost server)
    68         {
    69             return server.Services.GetService<IServer>().Features.Get<IServerAddressesFeature>().Addresses.First();
    70         }
    71     }
  • 相关阅读:
    javaScript
    CSS
    HTML
    折纸 (模拟)
    不等式(数学)
    周期串查询
    大集训模拟赛十一
    大假期集训模拟赛十
    P1631 序列合并
    KMP(烤馍片)算法
  • 原文地址:https://www.cnblogs.com/xuejietong/p/14336602.html
Copyright © 2011-2022 走看看