ASP.NET CORE 2.* 利用集成测试框架覆盖HttpClient相关代码

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

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

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

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

 

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

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

[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, 外部访问的地址须要是配置的app

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单元测试项目,添加以下包asp.net

Microsoft.AspNetCore.Appasync

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

 

原文出处:https://www.cnblogs.com/MindSharing/p/11283980.html

相关文章
相关标签/搜索