最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Asp.Net Core利用xUnit進(jìn)行主機(jī)級別的網(wǎng)絡(luò)集成測試詳解

 更新時(shí)間:2018年12月11日 10:24:44   作者:Ron.liang  
這篇文章主要給大家介紹了關(guān)于Asp.Net Core利用xUnit進(jìn)行主機(jī)級別的網(wǎng)絡(luò)集成測試的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們來一起看看吧

前言

在開發(fā) Asp.Net Core 應(yīng)用程序的過程中,我們常常需要對業(yè)務(wù)代碼編寫單元測試,這種方法既快速又有效,利用單元測試做代碼覆蓋測試,也是非常必要的事情;但是,但我們需要對系統(tǒng)進(jìn)行集成測試的時(shí)候,需要啟動(dòng)服務(wù)主機(jī),利用瀏覽器或者Postman 等網(wǎng)絡(luò)工具對接口進(jìn)行集成測試,這就非常的不方便,同時(shí)浪費(fèi)了大量的時(shí)間在重復(fù)啟動(dòng)應(yīng)用程序上;今天要介紹就是如何在不啟動(dòng)應(yīng)用程序的情況下,對 Asp.Net Core WebApi 項(xiàng)目進(jìn)行網(wǎng)絡(luò)集成測試。

一、建立項(xiàng)目

1.1 首先我們建立兩個(gè)項(xiàng)目,Asp.Net Core WebApi 和 xUnit 單元測試項(xiàng)目,如下

1.2 上圖的單元測試項(xiàng)目 Ron.XUnitTest 必須應(yīng)用待測試的 WebApi 項(xiàng)目 Ron.TestDemo

1.3 接下來打開 Ron.XUnitTest 項(xiàng)目文件 .csproj,添加包引用

Microsoft.AspNetCore.App
Microsoft.AspNetCore.TestHost

1.4 為什么要引用這兩個(gè)包呢,因?yàn)槲覄偛艅?chuàng)建的 WebApi 項(xiàng)目是引用 Microsoft.AspNetCore.App 的,至于 Microsoft.AspNetCore.TestHost,它是今天的主角,為了使用測試主機(jī),必須對其進(jìn)行引用,下面會(huì)詳細(xì)說明

二、編寫業(yè)務(wù)

2.1 創(chuàng)建一個(gè)接口,代碼如下

 [Route("api/[controller]")]
 [ApiController]
 public class ValuesController : ControllerBase
 {
 private IConfiguration configuration;
 public ValuesController(IConfiguration configuration)
 {
  this.configuration = configuration;
 }

 [HttpGet("{id}")]
 public ActionResult<int> Get(int id)
 {
  var result= id + this.configuration.GetValue<int>("max");

  return result;
 }
 }

2.1 接口代碼非常簡單,接受一個(gè)參數(shù) id,然后和配置文件中獲取的值 max 相加,然后輸出結(jié)果給客戶端

三、編寫測試用例

3.1 為了能夠使用主機(jī)集成測試,我們需要使用類

Microsoft.AspNetCore.TestHost.TestServer

3.2 我們來看一下 TestServer 的源碼,代碼較長,你可以直接跳過此段,進(jìn)入下一節(jié) 3.3

 public class TestServer : IServer
 {
 private IWebHost _hostInstance;
 private bool _disposed = false;
 private IHttpApplication<Context> _application;

 public TestServer(): this(new FeatureCollection())
 {
 }

 public TestServer(IFeatureCollection featureCollection)
 {
  Features = featureCollection ?? throw new ArgumentNullException(nameof(featureCollection));
 }

 public TestServer(IWebHostBuilder builder): this(builder, new FeatureCollection())
 {
 }
 
 public TestServer(IWebHostBuilder builder, IFeatureCollection featureCollection): this(featureCollection)
 {
  if (builder == null)
  {
  throw new ArgumentNullException(nameof(builder));
  }

  var host = builder.UseServer(this).Build();
  host.StartAsync().GetAwaiter().GetResult();
  _hostInstance = host;
 }

 public Uri BaseAddress { get; set; } = new Uri("http://localhost/");

 public IWebHost Host
 {
  get
  {
  return _hostInstance
   ?? throw new InvalidOperationException("The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available.");
  }
 }

 public IFeatureCollection Features { get; }

 private IHttpApplication<Context> Application
 {
  get => _application ?? throw new InvalidOperationException("The server has not been started or no web application was configured.");
 }

 public HttpMessageHandler CreateHandler()
 {
  var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
  return new ClientHandler(pathBase, Application);
 }

 public HttpClient CreateClient()
 {
  return new HttpClient(CreateHandler()) { BaseAddress = BaseAddress };
 }

 public WebSocketClient CreateWebSocketClient()
 {
  var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
  return new WebSocketClient(pathBase, Application);
 }

 public RequestBuilder CreateRequest(string path)
 {
  return new RequestBuilder(this, path);
 }

 public async Task<HttpContext> SendAsync(Action<HttpContext> configureContext, CancellationToken cancellationToken = default)
 {
  if (configureContext == null)
  {
  throw new ArgumentNullException(nameof(configureContext));
  }

  var builder = new HttpContextBuilder(Application);
  builder.Configure(context =>
  {
  var request = context.Request;
  request.Scheme = BaseAddress.Scheme;
  request.Host = HostString.FromUriComponent(BaseAddress);
  if (BaseAddress.IsDefaultPort)
  {
   request.Host = new HostString(request.Host.Host);
  }
  var pathBase = PathString.FromUriComponent(BaseAddress);
  if (pathBase.HasValue && pathBase.Value.EndsWith("/"))
  {
   pathBase = new PathString(pathBase.Value.Substring(0, pathBase.Value.Length - 1));
  }
  request.PathBase = pathBase;
  });
  builder.Configure(configureContext);
  return await builder.SendAsync(cancellationToken).ConfigureAwait(false);
 }

 public void Dispose()
 {
  if (!_disposed)
  {
  _disposed = true;
  _hostInstance.Dispose();
  }
 }

 Task IServer.StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
 {
  _application = new ApplicationWrapper<Context>((IHttpApplication<Context>)application, () =>
  {
  if (_disposed)
  {
   throw new ObjectDisposedException(GetType().FullName);
  }
  });

  return Task.CompletedTask;
 }

 Task IServer.StopAsync(CancellationToken cancellationToken)
 {
  return Task.CompletedTask;
 }

 private class ApplicationWrapper<TContext> : IHttpApplication<TContext>
 {
  private readonly IHttpApplication<TContext> _application;
  private readonly Action _preProcessRequestAsync;

  public ApplicationWrapper(IHttpApplication<TContext> application, Action preProcessRequestAsync)
  {
  _application = application;
  _preProcessRequestAsync = preProcessRequestAsync;
  }

  public TContext CreateContext(IFeatureCollection contextFeatures)
  {
  return _application.CreateContext(contextFeatures);
  }

  public void DisposeContext(TContext context, Exception exception)
  {
  _application.DisposeContext(context, exception);
  }

  public Task ProcessRequestAsync(TContext context)
  {
  _preProcessRequestAsync();
  return _application.ProcessRequestAsync(context);
  }
 }
 }

3.3 TestServer 類代碼量比較大,不過不要緊,我們只需要關(guān)注它的構(gòu)造方法就可以了

 public TestServer(IWebHostBuilder builder)
  : this(builder, new FeatureCollection())
 {
 }

3.4 其構(gòu)造方法接受一個(gè) IWebHostBuilder 對象,只要我們傳入一個(gè) WebHostBuilder 就可以創(chuàng)建一個(gè)測試主機(jī)了

3.5 創(chuàng)建測試主機(jī)和 HttpClient 客戶端,我們在測試類 ValuesUnitTest 編寫如下代碼

 public class ValuesUnitTest
 {
 private TestServer testServer;
 private HttpClient httpCLient;

 public ValuesUnitTest()
 {
  testServer = new TestServer(new WebHostBuilder().UseStartup<Ron.TestDemo.Startup>());
  httpCLient = testServer.CreateClient();
 }

 [Fact]
 public async void GetTest()
 {
  var data = await httpCLient.GetAsync("/api/values/100");
  var result = await data.Content.ReadAsStringAsync();

  Assert.Equal("300", result);
 }
 }

代碼解釋

這段代碼非常簡單,首先,我們聲明了一個(gè) TestServer 和 HttpClient 對象,并在構(gòu)造方法中初始化他們; TestServer 的初始化是由我們 new 了一個(gè) Builder 對象,并指定其使用待測試項(xiàng)目 Ron.TestDemo 中的 Startup 類來啟動(dòng),這樣我們能可以直接使用待測試項(xiàng)目的路由和管道了,甚至我們無需指定測試站點(diǎn),因?yàn)檫@些都會(huì)在 TestServer 自動(dòng)配置一個(gè) localhost 的主機(jī)地址

3.7 接下來就是創(chuàng)建了一個(gè)單元測試的方法,直接使用剛才初始化的 HttpClient 對象進(jìn)行網(wǎng)絡(luò)請求,這個(gè)時(shí)候,我們只需要知道 Action 即可,同時(shí)傳遞參數(shù) 100,最后斷言服務(wù)器輸出值為:"300",回顧一下我們創(chuàng)建的待測試方法,其業(yè)務(wù)正是將客戶端傳入的 id 值和配置文件 max 值相加后輸出,而 max 值在這里被配置為 200

3.8 運(yùn)行單元測試

3.9 測試通過,可以看到,測試達(dá)到了預(yù)期的結(jié)果,服務(wù)器正確返回了計(jì)算后的值

四、配置文件注意事項(xiàng)

4.1 在待測試項(xiàng)目中的配置文件 appsettings.json 并不會(huì)被測試主機(jī)所讀取,因?yàn)槲覀冊谏厦鎰?chuàng)建測試主機(jī)的時(shí)候沒有調(diào)用方法

WebHost.CreateDefaultBuilder

4.2 我們只是創(chuàng)建了一個(gè) WebHostBuilder 對象,非常輕量的主機(jī)配置,簡單來說就是無配置,如果對于 WebHost.CreateDefaultBuilder 不理解的同學(xué),建議閱讀我的文章 asp.netcore 深入了解配置文件加載過程.

4.3 所以,為了能夠在單元測試中使用項(xiàng)目配置文件,我在 Ron.TestDemo 項(xiàng)目中的 Startup 類加入了下面的代碼

 public class Startup
 {
 public Startup(IConfiguration configuration, IHostingEnvironment env)
 {
  this.Configuration = new ConfigurationBuilder()
  .AddJsonFile("appsettings.json")
  .AddEnvironmentVariables()
  .SetBasePath(env.ContentRootPath)
  .Build();
 }

 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.AddSingleton<IConfiguration>(this.Configuration);
  services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
 }
 }

4.4 其目的就是手動(dòng)讀取配置文件,重新初始化 IConfiguration 對象,并將 this.Configuration 對象加入依賴注入容器中

結(jié)語

  • 本文從單元測試入手,針對常見的系統(tǒng)集成測試提供了另外一種便捷的測試方案,通過創(chuàng)建 TestServer 測試主機(jī)開始,利用主機(jī)創(chuàng)建 HttpCLient 對象進(jìn)行網(wǎng)絡(luò)集成測試
  • 減少重復(fù)啟動(dòng)程序和測試工具,提高了測試效率
  • 充分利用了 Visual Studio 的優(yōu)勢,既可以做單元測試,還能利用這種測試方案進(jìn)行快速代碼調(diào)試
  • 最后,還了解如何通過 TestServer 主機(jī)加載待測試項(xiàng)目的配置文件對象 IConfiguration

示例代碼下載

http://xiazai.jb51.net/201812/yuanma/Ron.TestDemo_jb51.rar

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • LINQ操作數(shù)組代碼(交集,并集,差集,最值,平均,去重復(fù))

    LINQ操作數(shù)組代碼(交集,并集,差集,最值,平均,去重復(fù))

    數(shù)組是大學(xué)里經(jīng)常拿來做算法練習(xí)的對象。一些經(jīng)典算法非常有價(jià)值,考試、裝逼、面試都十分有用。但現(xiàn)在是效率時(shí)代,編程講究生產(chǎn)效率,利用LINQ,可以讓程序猿避免寫一些基本算法,把精力花在業(yè)務(wù)處理上
    2012-02-02
  • .NET?Core?Web?APi類庫內(nèi)嵌運(yùn)行的方法

    .NET?Core?Web?APi類庫內(nèi)嵌運(yùn)行的方法

    這篇文章主要介紹了.NET?Core?Web?APi類庫內(nèi)嵌運(yùn)行的方法,本節(jié)我們重點(diǎn)討論如何內(nèi)嵌運(yùn)行.NET Core Web APi類庫,同時(shí)介紹了兩種激活比如控制器特性方案,需要的朋友可以參考下
    2022-09-09
  • .Net極限生產(chǎn)力之分表分庫全自動(dòng)化Migrations?Code-First

    .Net極限生產(chǎn)力之分表分庫全自動(dòng)化Migrations?Code-First

    這篇文章主要介紹了.Net極限生產(chǎn)力之分表分庫全自動(dòng)化Migrations?Code-First,輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學(xué)習(xí)成本、零業(yè)務(wù)代碼入侵適配
    2022-07-07
  • javascript判斷是否有對RadioButtonList選項(xiàng)選擇

    javascript判斷是否有對RadioButtonList選項(xiàng)選擇

    寫個(gè)Javascript來判斷是否有對RadioButtonList選項(xiàng)選擇,附動(dòng)畫演示,感興趣的朋友可以了解下,希望對您們有幫助
    2013-01-01
  • LINQ to XML的編程基礎(chǔ)

    LINQ to XML的編程基礎(chǔ)

    本文介紹了LINQ to XML的編程基礎(chǔ),即System.Xml.Linq命名空間中的多個(gè)LINQ to XML類,這些類都是LINQ to XML的支持類,它們使得處理xml比使用其他的xml工具容易得多。在本文中,著重介紹的是XElement、XAttribute和XDocument。
    2010-02-02
  • ASP.NET對SQLServer的通用數(shù)據(jù)庫訪問類

    ASP.NET對SQLServer的通用數(shù)據(jù)庫訪問類

    這篇文章主要實(shí)現(xiàn)了ASP.NET對SQLServer的通用數(shù)據(jù)庫訪問類
    2016-02-02
  • ASP.NET Core利用Jaeger實(shí)現(xiàn)分布式追蹤詳解

    ASP.NET Core利用Jaeger實(shí)現(xiàn)分布式追蹤詳解

    這篇文章主要給大家介紹了關(guān)于ASP.NET Core利用Jaeger實(shí)現(xiàn)分布式追蹤的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用ASP.NET Core具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • WPF依賴屬性用法詳解

    WPF依賴屬性用法詳解

    本文詳細(xì)講解了WPF依賴屬性的用法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • MvcPager分頁控件使用注意事項(xiàng)

    MvcPager分頁控件使用注意事項(xiàng)

    這篇文章主要為大家詳細(xì)介紹了MvcPager分頁控件使用的注意事項(xiàng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • xUnit 編寫 ASP.NET Core 單元測試的方法

    xUnit 編寫 ASP.NET Core 單元測試的方法

    這篇文章主要介紹了xUnit 編寫 ASP.NET Core 單元測試的方法,文中代碼非常詳細(xì),幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06

最新評論

富川| 甘洛县| 清河县| 尤溪县| 敦煌市| 长兴县| 九龙坡区| 密山市| 眉山市| 嘉义县| 乌拉特前旗| 伊通| 克什克腾旗| 闻喜县| 高青县| 内乡县| 宜城市| 平邑县| 沂水县| 历史| 开化县| 鸡西市| 德昌县| 伊春市| 镇康县| 万盛区| 乐至县| 丹巴县| 融水| 南城县| 龙海市| 喀喇沁旗| 香港| 海兴县| 搜索| 仲巴县| 文昌市| 巢湖市| 平泉县| 卢湾区| 太白县|