我見過這個問題出現在幾個地方,沒有看到任何好的答案。由於我必須自己做幾次,所以我想我會發布我的解決方案。如果你有什麼更好的,請發佈。我如何測試ASP.NET MVC 4 Web API中的自定義DelegatingHandler?
N.B.這是使用ASP.NET MVC 4 Beta 2版本的Web API - 未來的版本可能會改變!
更新:這仍然工作在ASP.NET MVC 4 RC
我見過這個問題出現在幾個地方,沒有看到任何好的答案。由於我必須自己做幾次,所以我想我會發布我的解決方案。如果你有什麼更好的,請發佈。我如何測試ASP.NET MVC 4 Web API中的自定義DelegatingHandler?
N.B.這是使用ASP.NET MVC 4 Beta 2版本的Web API - 未來的版本可能會改變!
更新:這仍然工作在ASP.NET MVC 4 RC
在這種方法中,我創建了一個TestHandler並將其設置爲處理程序的被測InnerHandler
財產。
被測試的處理程序然後可以被傳遞給HttpClient
- 如果您正在編寫服務器端處理程序,這看起來可能不直觀,但這實際上是測試處理程序的一種很好的輕量級方式 - 它將被調用就像在服務器中一樣。
TestHandler默認會返回一個HTTP 200,但它的構造函數接受一個函數,可以使用這個函數來對來自被測試的處理程序的 傳入的請求消息進行斷言。最後,您可以對來自客戶端的SendAsync調用的結果進行斷言。
一旦完成所有設置,請在客戶端實例上調用SendAsync
以調用您的處理程序。請求將傳遞到你的處理程序中,它將把它傳遞給TestHandler(假設它通過調用),然後返回一個響應給你的處理程序。
測試處理機是這樣的:
public class TestHandler : DelegatingHandler
{
private readonly Func<HttpRequestMessage,
CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public TestHandler()
{
_handlerFunc = (r, c) => Return200();
}
public TestHandler(Func<HttpRequestMessage,
CancellationToken, Task<HttpResponseMessage>> handlerFunc)
{
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
public static Task<HttpResponseMessage> Return200()
{
return Task.Factory.StartNew(
() => new HttpResponseMessage(HttpStatusCode.OK));
}
}
實施例使用與被測試的假想MyHandler
。使用NUnit的爲斷言:
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
httpRequestMessage.Headers.Add("username", "test");
var handler = new MyHandler()
{
InnerHandler = new TestHandler((r,c) =>
{
Assert.That(r.Headers.Contains("username"));
return TestHandler.Return200();
})
};
var client = new HttpClient(handler);
var result = client.SendAsync(httpRequestMessage).Result;
Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK));
TestHandler的默認行爲可能是許多試驗和罰款,使代碼更簡單。處理程序的測試設置,然後看起來是這樣的:
var handler = new MyHandler();
handler.InnerHandler = new TestHandler();
我喜歡這種方法,因爲它使測試方法中的所有斷言,和TestHandler
非常重用的。
我創建了以下用於測試DelegatingHandlers。對於使用HttpRequestMessage.DependencyScope來處理使用您最喜歡的IoC框架的依賴關係的處理程序非常有用,例如有WindsorContainer一個WindsorDependencyResolver:
public class UnitTestHttpMessageInvoker : HttpMessageInvoker
{
private readonly HttpConfiguration configuration;
public UnitTestHttpMessageInvoker(HttpMessageHandler handler, IDependencyResolver resolver)
: base(handler, true)
{
this.configuration = new HttpConfiguration();
configuration.DependencyResolver = resolver;
}
[DebuggerNonUserCode]
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
request.Properties["MS_HttpConfiguration"] = this.configuration;
return base.SendAsync(request, cancellationToken);
}
}
我只是在尋找同樣的事情,但與沒有使用HTTP客戶端更簡潔的方式上來。我想要一個測試來斷言消息處理程序使用了一個模擬日誌記錄組件。我並不是真的需要內部處理函數來運行,只是爲了「存根」它來滿足單元測試。適用於我的目的:)
//ARRANGE
var logger = new Mock<ILogger>();
var handler= new ServiceLoggingHandler(logger.Object);
var request = ControllerContext.CreateHttpRequest(Guid.NewGuid(), "http://test",HttpMethod.Get);
handler.InnerHandler = new Mock<HttpMessageHandler>(MockBehavior.Loose).Object;
request.Content = new ObjectContent<CompanyRequest>(Company.CreateCompanyDTO(), new JsonMediaTypeFormatter());
var invoker = new HttpMessageInvoker(handler);
//ACT
var result = invoker.SendAsync(request, new System.Threading.CancellationToken()).Result;
//ASSERT
<Your assertion>
我也發現了這個答案,因爲我有我的自定義處理程序,我想測試一下 我們使用NUnit和起訂量,所以我覺得我的解決方案可能是有人
有幫助using Moq;
using Moq.Protected;
using NUnit.Framework;
namespace Unit.Tests
{
[TestFixture]
public sealed class Tests1
{
private HttpClient _client;
private HttpRequestMessage _httpRequest;
private Mock<DelegatingHandler> _testHandler;
private MyCustomHandler _subject;//MyCustomHandler inherits DelegatingHandler
[SetUp]
public void Setup()
{
_httpRequest = new HttpRequestMessage(HttpMethod.Get, "/someurl");
_testHandler = new Mock<DelegatingHandler>();
_subject = new MyCustomHandler // create subject
{
InnerHandler = _testHandler.Object //initialize InnerHandler with our mock
};
_client = new HttpClient(_subject)
{
BaseAddress = new Uri("http://localhost")
};
}
[Test]
public async Task Given_1()
{
var mockedResult = new HttpResponseMessage(HttpStatusCode.Accepted);
void AssertThatRequestCorrect(HttpRequestMessage request, CancellationToken token)
{
Assert.That(request, Is.SameAs(_httpRequest));
//... Other asserts
}
// setup protected SendAsync
// our MyCustomHandler will call SendAsync internally, and we want to check this call
_testHandler
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", _httpRequest, ItExpr.IsAny<CancellationToken>())
.Callback(
(Action<HttpRequestMessage, CancellationToken>)AssertThatRequestCorrect)
.ReturnsAsync(mockedResult);
//Act
var actualResponse = await _client.SendAsync(_httpRequest);
//check that internal call to SendAsync was only Once and with proper request object
_testHandler
.Protected()
.Verify("SendAsync", Times.Once(), _httpRequest, ItExpr.IsAny<CancellationToken>());
// if our custom handler modifies somehow our response we can check it here
Assert.That(actualResponse.IsSuccessStatusCode, Is.True);
Assert.That(actualResponse, Is.EqualTo(mockedResult));
//...Other asserts
}
}
}
你如何處理這裏的請求對象。由於沒有httpconfiguration,request.createresponse失敗。所以如果你添加一個你可以得到的,但是如果你對與你創建的httprequestmessage相關的其他請求屬性感興趣呢? – Steve 2013-01-18 01:53:25
我這裏的目標純粹是爲了測試處理程序;那麼你的建議是否足夠添加HttpConfiguration?你的場景是什麼? – 2013-01-24 00:52:24
我不得不在request.content,request.properties和request.getroutedata中添加包裝。一切都很好,在測試landia – Steve 2013-01-24 01:11:49