您的位置:首页 > 理论基础 > 计算机网络

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

2019-08-16 15:59 1421 查看

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

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: