zoukankan      html  css  js  c++  java
  • ASP.NET CORE 2.* 利用集成测试框架覆盖HttpClient相关代码

    ASP.NET CORE 集成测试官方介绍

    我的asp.net core 项目里面大部分功能都是去调用别人的API ,大量使用HttpClient,公司单元测试覆盖率要求95%以上,很难做到不mock HttpClient 达到这个指数。

    以下方法是我自己总结的在单元测试里 mock httpClient 的方式,基本思路是利用集成测试框架,mock外部调用的API ,达到httpClient 代码的覆盖。

    代码地址:https://github.com/Halo-Shaka/LearningAspNetCoreIntegrationTesting.git

    举个例子,创建一个简单的asp.net core 项目,里面只有一个api , api/values, 是个get 方法,

    get 方法内部是去调用外部API, 随便写个方法  向google 发一个信息。

       [Route("api/[controller]")]
        [ApiController]
        public class ValuesController : ControllerBase
        {
            private readonly IHttpClientFactory _httpClientFactory;
    
            private readonly IOptions<AppSettings> _options;
    
            public ValuesController(IHttpClientFactory httpClientFactory, IOptions<AppSettings> options)
            {
                _httpClientFactory = httpClientFactory;
                _options = options;
            }
    
            // GET api/values
            [HttpGet]
            public async Task<ActionResult> Get()
            {
                var client = _httpClientFactory.CreateClient();
    
                var url = _options.Value.Url;
                var payload = new
                {
                    From = "China"
                };
    
                var requestMessage = new HttpRequestMessage(HttpMethod.Post, url)
                {
                    Content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json")
                };
    
                try
                {
                    var response = await client.SendAsync(requestMessage);
                    var content = await response.Content.ReadAsStringAsync();
    
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        return Ok(content);
                    }
    
                    return BadRequest();
                }
                catch (Exception e)
                {
                    return StatusCode(502);
                }
            }
        }
    

      

    这里面有个需要注意的地方,使用注入的httpClient, 外部访问的地址需要是配置的

     public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
                services.AddHttpClient();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                    app.UseHsts();
                }
    
                app.UseHttpsRedirection();
                app.UseMvc();
            }
        }
    

      

    到此为止,基本功能就写完了,现在来写测试代码 

    添加 XUnit单元测试项目,添加如下包

    Microsoft.AspNetCore.App

    Microsoft.AspNetCore.Mvc.Testing

    Microsoft.NET.Test.Sdk

    Moq

    利用集成测试的虚拟站点,把我们需要调用的外部API 伪造出来,

     [Route("gateway")]
        public class MockGatewayController : ControllerBase
        {
            [HttpPost]
            public ActionResult<string> Logon([FromBody]LogonRequest request)
            {
                if (request.From == "China")
                {
                    var behavior = MockGatewayData.MockBehavior;
                    return behavior.LogonResult();
                }
    
                return string.Empty;
            }
        }
    
        public class LogonRequest
        {
            public string From { get; set; }
        }
    
        public interface IGatewayMockBehavior
        {
            ActionResult<string> LogonResult();
        }
    
        public class MockGatewayData
        {
            public static IGatewayMockBehavior MockBehavior { get; set; }
        }
    MockGatewayData类的作用是 让客户端能够访问到服务端,并指定想要返回的结果
    接着创建 GenericWebApplicationFactory,并把刚伪造的 controller 指定到虚拟站点里面,

        public class GenericWebApplicationFactory : WebApplicationFactory<Startup>
        {
            protected override void ConfigureWebHost(IWebHostBuilder builder)
            {
                builder.ConfigureServices(services =>
                {
                    services.AddMvc().AddApplicationPart(typeof(MockGatewayController).Assembly).AddControllersAsServices();
                });
            }
        }

    最后写测试代码

     public class ValuesControllerTest : IClassFixture<GenericWebApplicationFactory>
        {
            public ValuesControllerTest(GenericWebApplicationFactory factory, ITestOutputHelper output)
            {
                this.factory = factory;
                this.output = output;
            }
    
            protected GenericWebApplicationFactory factory;
            protected ITestOutputHelper output;
    
    
            [Fact]
            public void GetRequest_GatewayInaccessible_ShouldReturn502()
            {
                var client = factory.WithWebHostBuilder(p => p.ConfigureServices(services =>
                {
                    services.PostConfigure<AppSettings>(options => { options.Url = "https://aaaaaaaa"; });
                })).CreateClient();
                var response = client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "api/values")).Result;
                Assert.Equal(HttpStatusCode.BadGateway, response.StatusCode);
            }
    
            [Fact]
            public void GetRequest_GatewayOnFailed_ShouldReturn400()
            {
                var behavior = new Mock<IGatewayMockBehavior>();
                behavior.Setup(p => p.LogonResult()).Returns(new BadRequestResult());
                MockGatewayData.MockBehavior = behavior.Object;
    
                var client = CreateHttpClient();
                var response = client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "api/values")).Result;
                Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
            }
    
            [Fact]
            public void GetRequest_GatewayOnSuccess_ShouldReturn200()
            {
                var behavior = new Mock<IGatewayMockBehavior>();
                behavior.Setup(p => p.LogonResult()).Returns(new ActionResult<string>("success"));
                MockGatewayData.MockBehavior = behavior.Object;
    
                var client = CreateHttpClient();
                var response = client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "api/values")).Result;
                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            }
    
            private HttpClient CreateHttpClient()
            {
                var client = factory.WithWebHostBuilder(p => p.ConfigureServices(services =>
                {
                    services.PostConfigure<AppSettings>(options => { options.Url = "http://localhost/gateway"; });
    
                    services.AddSingleton(typeof(IHttpClientFactory), new MockHttpClientFactory
                    {
                        InjectHttpClient = factory.CreateClient
                    });
                })).CreateClient();
    
                return client;
            }
        }

    最后看下覆盖率,整个controller 里面httpClient  全都被覆盖了

    代码地址:

    https://github.com/Halo-Shaka/LearningAspNetCoreIntegrationTesting.git

  • 相关阅读:
    [十七]SpringBoot 之 使用自定义的properties
    【转】手摸手,带你用vue撸后台 系列三(实战篇)
    【转】手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)
    【转】手摸手,带你用vue撸后台 系列二(登录权限篇)
    【转】手摸手,带你用vue撸后台 系列一
    【16】vuex2.0 之 getter
    【15】vuex2.0 之 modules
    【14】vuex2.0 之 mutation 和 action
    【13】vuex2.0 之 state
    【12】vue-router 之路由重定向
  • 原文地址:https://www.cnblogs.com/MindSharing/p/11283980.html
Copyright © 2011-2022 走看看