Wie auch in den Kommentaren erwähnt müssen Sie abstrakt weg die HttpClient
es so nicht zu koppeln. Ich habe in der Vergangenheit etwas Ähnliches gemacht. Ich werde versuchen, das, was ich getan habe, an das anzupassen, was Sie versuchen zu tun.
Schauen Sie sich zuerst die HttpClient
Klasse an und entscheiden Sie, welche Funktionalität sie benötigt.
Hier ist eine Möglichkeit:
public interface IHttpClient {
System.Threading.Tasks.Task<T> DeleteAsync<T>(string uri) where T : class;
System.Threading.Tasks.Task<T> DeleteAsync<T>(Uri uri) where T : class;
System.Threading.Tasks.Task<T> GetAsync<T>(string uri) where T : class;
System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class;
System.Threading.Tasks.Task<T> PostAsync<T>(string uri, object package);
System.Threading.Tasks.Task<T> PostAsync<T>(Uri uri, object package);
System.Threading.Tasks.Task<T> PutAsync<T>(string uri, object package);
System.Threading.Tasks.Task<T> PutAsync<T>(Uri uri, object package);
}
Wie bereits erwähnt, war dies wiederum für bestimmte Zwecke. Ich habe die meisten Abhängigkeiten von allem, was damit zu tun hat, vollständig abstrahiert HttpClient
und mich auf das konzentriert, was ich zurückgeben wollte. Sie sollten bewerten, wie Sie das abstrahieren möchten, HttpClient
um nur die erforderliche Funktionalität bereitzustellen.
Auf diese Weise können Sie jetzt nur das verspotten, was zum Testen benötigt wird.
Ich würde sogar empfehlen, ganz abzuschaffen IHttpHandler
und die HttpClient
Abstraktion zu verwenden IHttpClient
. Aber ich wähle einfach nicht aus, da Sie den Hauptteil Ihrer Handler-Schnittstelle durch die Mitglieder des abstrahierten Clients ersetzen können.
Eine Implementierung von IHttpClient
kann dann verwendet werden, um ein reales / konkretes HttpClient
oder ein anderes Objekt für diese Angelegenheit zu verpacken / anzupassen , das verwendet werden kann, um HTTP-Anforderungen zu stellen, da das, was Sie wirklich wollten, ein Dienst war, der diese Funktionalität gemäß den HttpClient
spezifischen Anforderungen bereitstellte . Die Verwendung der Abstraktion ist ein sauberer (meiner Meinung nach) und SOLID-Ansatz und kann Ihren Code wartbarer machen, wenn Sie den zugrunde liegenden Client gegen etwas anderes austauschen müssen, wenn sich das Framework ändert.
Hier ist ein Ausschnitt davon, wie eine Implementierung durchgeführt werden könnte.
/// <summary>
/// HTTP Client adaptor wraps a <see cref="System.Net.Http.HttpClient"/>
/// that contains a reference to <see cref="ConfigurableMessageHandler"/>
/// </summary>
public sealed class HttpClientAdaptor : IHttpClient {
HttpClient httpClient;
public HttpClientAdaptor(IHttpClientFactory httpClientFactory) {
httpClient = httpClientFactory.CreateHttpClient(**Custom configurations**);
}
//...other code
/// <summary>
/// Send a GET request to the specified Uri as an asynchronous operation.
/// </summary>
/// <typeparam name="T">Response type</typeparam>
/// <param name="uri">The Uri the request is sent to</param>
/// <returns></returns>
public async System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class {
var result = default(T);
//Try to get content as T
try {
//send request and get the response
var response = await httpClient.GetAsync(uri).ConfigureAwait(false);
//if there is content in response to deserialize
if (response.Content.Headers.ContentLength.GetValueOrDefault() > 0) {
//get the content
string responseBodyAsText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
//desrialize it
result = deserializeJsonToObject<T>(responseBodyAsText);
}
} catch (Exception ex) {
Log.Error(ex);
}
return result;
}
//...other code
}
Wie Sie im obigen Beispiel sehen können, ist ein Großteil des schweren Hebens, das normalerweise mit der Verwendung verbunden HttpClient
ist, hinter der Abstraktion verborgen.
Ihre Verbindungsklasse kann dann mit dem abstrahierten Client injiziert werden
public class Connection
{
private IHttpClient _httpClient;
public Connection(IHttpClient httpClient)
{
_httpClient = httpClient;
}
}
Ihr Test kann dann verspotten, was für Ihr SUT benötigt wird
private IHttpClient _httpClient;
[TestMethod]
public void TestMockConnection()
{
SomeModelObject model = new SomeModelObject();
var httpClientMock = new Mock<IHttpClient>();
httpClientMock.Setup(c => c.GetAsync<SomeModelObject>(It.IsAny<string>()))
.Returns(() => Task.FromResult(model));
_httpClient = httpClientMock.Object;
var client = new Connection(_httpClient);
// Assuming doSomething uses the client to make
// a request for a model of type SomeModelObject
client.doSomething();
}
HttpClient
in Ihrer Benutzeroberfläche anzeigen , liegt das Problem darin. Sie zwingen Ihren Client, dieHttpClient
konkrete Klasse zu verwenden. Stattdessen sollten Sie eine Abstraktion desHttpClient
.