之前如果對 ASP.NET WebAPI 進行單元測試(HttpClient 發起請求,并且可調試 WebAPI),一般采用 Owin 的方式,具體參考:《開發筆記:用 Owin Host 實現脫離 IIS 跑 Web API 單元測試》
示例代碼:
public class ValuesWebApiTest : IDisposable
{
private const string HOST_ADDRESS = "http://localhost:8001";
private IDisposable _webApp;
private HttpClient _httClient;
public AdTextUnitWebApiTest()
{
_webApp = WebApp.Start<Startup>(HOST_ADDRESS);
Console.WriteLine("Web API started!");
_httClient = new HttpClient();
_httClient.BaseAddress = new Uri(HOST_ADDRESS);
Console.WriteLine("HttpClient started!");
}
[Fact]
public async Task Get()
{
var response = await _httClient.GetAsync("/api/values");
if (response.StatusCode != HttpStatusCode.OK)
{
Console.WriteLine(response.StatusCode);
Console.WriteLine((await response.Content.ReadAsAsync<HttpError>()).ExceptionMessage);
}
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var test = await response.Content.ReadAsStringAsync();
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
public void Dispose()
{
_httClient.Dispose();
_webApp.Dispose();
}
}
本來想在 ASP.NET 5 WebAPI 項目中,也用這一套測試代碼,但發現并不適用,因為 ASP.NET WebAPI 2 和 ASP.NET 5 WebAPI 并不是特別一樣,比如 Startup.cs 的配置等等,之前使用 WebApp.Start<Startup>(HOST_ADDRESS)
的方式啟動 WebAPI 項目,而 ASP.NET 5 WebAPI 變成了這樣的:
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
想用 WebApplication.Run
的方式替換掉 WebApp.Start
,但發現并不可行,比如 args 的參數問題,自己想的有點簡單了,后來 Google 搜索了一些資料,發現 ASP.NET 5 增加了 TestServer,自己找資料配置了很久,看別人的示例代碼很簡單,但我運行的時候就是各種報錯,主要原因是程序包的版本不對,因為我是按照 project.json 的提示安裝的,比如 Microsoft.AspNet.TestHost
這個程序包,提示最新版本為 1.0.0-rc2-15960
,并且沒有 1.0.0-rc1-final
版本,然后我就安裝提示安裝的 rc2,就報下面的異常:
異常信息:Could not load type 'Microsoft.AspNet.Builder.RequestDelegate' from assembly 'Microsoft.AspNet.Http.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
根據提示,我以為異常原因是沒有加載 Microsoft.AspNet.Http.Abstractions
程序集,然后又添加此程序集,重新運行發現還是報錯。。。后面具體的過程就不記錄了,反正坑很大,根本原因是 Microsoft.AspNet.TestHost
程序包的版本不對,應該安裝 1.0.0-rc1-final
版本,我是后來無意間重啟 VS2015 發現的。
下面貼一下 ASP.NET 5 進行單元測試的一些代碼。
首先 ASP.NET 5 WebAPI 項目 Startup.cs 配置代碼:
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Demo.WebApi
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIISPlatformHandler();
app.UseStaticFiles();
app.UseMvc();
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
}
ValuesWebApiTest 測試代碼:
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Demo.WebApiTests
{
public class ValuesWebApiTest
{
public TestServer _server;
public ValuesWebApiTest()
{
_server = TestServer.Create(app =>
{
var env = app.ApplicationServices.GetRequiredService<IHostingEnvironment>();
var loggerFactory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
new CNBlogs.Ad.WebApi.Startup(env).Configure(app, env, loggerFactory);
}, services =>
{
services.AddMvc();
services.Configure();
});
}
}
[Fact]
public async Task Get()
{
var response = await _server.CreateClient().GetAsync("/api/values");
if (response.StatusCode != HttpStatusCode.OK)
{
Console.WriteLine(response.StatusCode);
Console.WriteLine((await response.Content.ReadAsAsync<HttpError>()).ExceptionMessage);
}
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var test = await response.Content.ReadAsStringAsync();
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
project.json 配置代碼:
{
"frameworks": {
"dnx451": { }
},
"dependencies": {
"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-rc1-final",
"Microsoft.Net.Http": "2.2.29",
"Microsoft.AspNet.TestHost": "1.0.0-rc1-final",
"xunit": "2.1.0",
"xunit.runner.dnx": "2.1.0-rc1-build204"
},
"commands": {
"test": "xunit.runner.dnx"
}
}
運行測試成功,并且可以 Debug 調試,需要注意 using 引用(沒用的我都去掉了),還有程序包的版本號。
注:如果 VS2015 Test Explorer 中找不到測試示例,需要安裝最新的 xUnit 程序包。
"xunit": "2.2.0-beta2-build3256",
"xunit.runner.dnx": "2.1.0-rc2-build209"
xUnit 程序包地址:http://myget.org/gallery/xunit
參考資料:
文章列表