![](/img/trans.png)
[英]How to Unit Test a custom JsonConverter in ASP.NET Web API?
[英]How can you unit test ASP.NET Web API routing?
我正在嘗試編寫一些單元測試,以確保將對Web API的請求路由到具有預期參數的預期API控制器操作。
我嘗試使用HttpServer
類創建測試,但是從服務器獲得500個響應,並且沒有任何信息可以調試問題。
有沒有一種方法可以為ASP.NET Web API網站的路由創建單元測試?
理想情況下,我想使用HttpClient
創建一個請求,並讓服務器處理該請求,並將其通過預期的路由過程。
我寫了一篇有關測試路線並做您所想做的事情的博客文章:
http://www.strathweb.com/2012/08/testing-routes-in-asp-net-web-api/
希望能幫助到你。
另一個優點是,我使用反射來提供操作方法-因此,您不必以強類型方式添加它們,而不是使用帶有字符串的路由。 使用這種方法,如果您的操作名稱發生更改,則測試不會編譯,因此您可以輕松發現錯誤。
測試ASP.NET Web API應用程序路由的最佳方法是對端點進行集成測試。
這是您的ASP.NET Web API應用程序的簡單集成測試示例。 這主要不是測試您的路線,而是無形地測試它們。 另外,我在這里使用XUnit,Autofac和Moq。
[Fact, NullCurrentPrincipal]
public async Task
Returns_200_And_Role_With_Key() {
// Arrange
Guid key1 = Guid.NewGuid(),
key2 = Guid.NewGuid(),
key3 = Guid.NewGuid(),
key4 = Guid.NewGuid();
var mockMemSrv = ServicesMockHelper
.GetInitialMembershipService();
mockMemSrv.Setup(ms => ms.GetRole(
It.Is<Guid>(k =>
k == key1 || k == key2 ||
k == key3 || k == key4
)
)
).Returns<Guid>(key => new Role {
Key = key, Name = "FooBar"
});
var config = IntegrationTestHelper
.GetInitialIntegrationTestConfig(GetInitialServices(mockMemSrv.Object));
using (var httpServer = new HttpServer(config))
using (var client = httpServer.ToHttpClient()) {
var request = HttpRequestMessageHelper
.ConstructRequest(
httpMethod: HttpMethod.Get,
uri: string.Format(
"https://localhost/{0}/{1}",
"api/roles",
key2.ToString()),
mediaType: "application/json",
username: Constants.ValidAdminUserName,
password: Constants.ValidAdminPassword);
// Act
var response = await client.SendAsync(request);
var role = await response.Content.ReadAsAsync<RoleDto>();
// Assert
Assert.Equal(key2, role.Key);
Assert.Equal("FooBar", role.Name);
}
}
我為此測試使用了一些外部幫助程序。 其中之一是NullCurrentPrincipalAttribute
。 由於測試將在Windows身份下運行,因此將使用該身份設置Thread.CurrentPrincipal
。 因此,如果您在應用程序中使用某種授權,則最好首先消除此授權:
public class NullCurrentPrincipalAttribute : BeforeAfterTestAttribute {
public override void Before(MethodInfo methodUnderTest) {
Thread.CurrentPrincipal = null;
}
}
然后,創建一個模擬MembershipService
。 這是特定於應用程序的設置。 因此,將針對您自己的實現進行更改。
GetInitialServices
為我創建了Autofac容器。
private static IContainer GetInitialServices(
IMembershipService memSrv) {
var builder = IntegrationTestHelper
.GetEmptyContainerBuilder();
builder.Register(c => memSrv)
.As<IMembershipService>()
.InstancePerApiRequest();
return builder.Build();
}
GetInitialIntegrationTestConfig
方法只是初始化我的配置。
internal static class IntegrationTestHelper {
internal static HttpConfiguration GetInitialIntegrationTestConfig() {
var config = new HttpConfiguration();
RouteConfig.RegisterRoutes(config.Routes);
WebAPIConfig.Configure(config);
return config;
}
internal static HttpConfiguration GetInitialIntegrationTestConfig(IContainer container) {
var config = GetInitialIntegrationTestConfig();
AutofacWebAPI.Initialize(config, container);
return config;
}
}
RouteConfig.RegisterRoutes
方法基本上注冊了我的路由。 我還有一個擴展方法,可以在HttpServer
上創建HttpClient
。
internal static class HttpServerExtensions {
internal static HttpClient ToHttpClient(
this HttpServer httpServer) {
return new HttpClient(httpServer);
}
}
最后,我有一個名為HttpRequestMessageHelper
的靜態類,該類具有一堆用於構造新HttpRequestMessage
實例的靜態方法。
internal static class HttpRequestMessageHelper {
internal static HttpRequestMessage ConstructRequest(
HttpMethod httpMethod, string uri) {
return new HttpRequestMessage(httpMethod, uri);
}
internal static HttpRequestMessage ConstructRequest(
HttpMethod httpMethod, string uri, string mediaType) {
return ConstructRequest(
httpMethod,
uri,
new MediaTypeWithQualityHeaderValue(mediaType));
}
internal static HttpRequestMessage ConstructRequest(
HttpMethod httpMethod, string uri,
IEnumerable<string> mediaTypes) {
return ConstructRequest(
httpMethod,
uri,
mediaTypes.ToMediaTypeWithQualityHeaderValues());
}
internal static HttpRequestMessage ConstructRequest(
HttpMethod httpMethod, string uri, string mediaType,
string username, string password) {
return ConstructRequest(
httpMethod, uri, new[] { mediaType }, username, password);
}
internal static HttpRequestMessage ConstructRequest(
HttpMethod httpMethod, string uri,
IEnumerable<string> mediaTypes,
string username, string password) {
var request = ConstructRequest(httpMethod, uri, mediaTypes);
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic",
EncodeToBase64(
string.Format("{0}:{1}", username, password)));
return request;
}
// Private helpers
private static HttpRequestMessage ConstructRequest(
HttpMethod httpMethod, string uri,
MediaTypeWithQualityHeaderValue mediaType) {
return ConstructRequest(
httpMethod,
uri,
new[] { mediaType });
}
private static HttpRequestMessage ConstructRequest(
HttpMethod httpMethod, string uri,
IEnumerable<MediaTypeWithQualityHeaderValue> mediaTypes) {
var request = ConstructRequest(httpMethod, uri);
request.Headers.Accept.AddTo(mediaTypes);
return request;
}
private static string EncodeToBase64(string value) {
byte[] toEncodeAsBytes = Encoding.UTF8.GetBytes(value);
return Convert.ToBase64String(toEncodeAsBytes);
}
}
我在我的應用程序中使用基本身份驗證。 因此,此類具有一些使用Authentication標頭構造HttpRequestMessege
方法。
最后,我做我的行為和斷言來驗證我需要的東西。 這可能是一個大材小用的示例,但我認為這將為您提供一個好主意。
這是有關HttpServer集成測試的精彩博客文章。 另外,這是有關在ASP.NET Web API中測試路由的另一篇好文章。
嗨,當您要測試路由時,主要目標是通過此測試測試GetRouteData(),以確保路由系統正確識別您的請求並選擇了正確的路由。
[Theory]
[InlineData("http://localhost:5240/foo/route", "GET", false, null, null)]
[InlineData("http://localhost:5240/api/Cars/", "GET", true, "Cars", null)]
[InlineData("http://localhost:5240/api/Cars/123", "GET", true, "Cars", "123")]
public void DefaultRoute_Returns_Correct_RouteData(
string url, string method, bool shouldfound, string controller, string id)
{
//Arrange
var config = new HttpConfiguration();
WebApiConfig.Register(config);
var actionSelector = config.Services.GetActionSelector();
var controllerSelector = config.Services.GetHttpControllerSelector();
var request = new HttpRequestMessage(new HttpMethod(method), url);
config.EnsureInitialized();
//Act
var routeData = config.Routes.GetRouteData(request);
//Assert
// assert
Assert.Equal(shouldfound, routeData != null);
if (shouldfound)
{
Assert.Equal(controller, routeData.Values["controller"]);
Assert.Equal(id == null ? (object)RouteParameter.Optional : (object)id, routeData.
Values["id"]);
}
}
這很重要,但還不夠,即使不檢查是否選擇了正確的路由並提取了正確的路由數據也不能確保選擇了正確的控制器和操作,如果不重寫默認的IHttpActionSelector和IHttpControllerSelector服務,這是一種方便的方法。與你自己的。
[Theory]
[InlineData("http://localhost:12345/api/Cars/123", "GET", typeof(CarsController), "GetCars")]
[InlineData("http://localhost:12345/api/Cars", "GET", typeof(CarsController), "GetCars")]
public void Ensure_Correct_Controller_and_Action_Selected(string url,string method,
Type controllerType,string actionName) {
//Arrange
var config = new HttpConfiguration();
WebApiConfig.Register(config);
var controllerSelector = config.Services.GetHttpControllerSelector();
var actionSelector = config.Services.GetActionSelector();
var request = new HttpRequestMessage(new HttpMethod(method),url);
config.EnsureInitialized();
var routeData = config.Routes.GetRouteData(request);
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
//Act
var ctrlDescriptor = controllerSelector.SelectController(request);
var ctrlContext = new HttpControllerContext(config, routeData, request)
{
ControllerDescriptor = ctrlDescriptor
};
var actionDescriptor = actionSelector.SelectAction(ctrlContext);
//Assert
Assert.NotNull(ctrlDescriptor);
Assert.Equal(controllerType, ctrlDescriptor.ControllerType);
Assert.Equal(actionName, actionDescriptor.ActionName);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.