Wie auch in den Kommentaren erwähnt müssen Sie abstrakt weg die HttpClientes 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 HttpClientKlasse 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 HttpClientund mich auf das konzentriert, was ich zurückgeben wollte. Sie sollten bewerten, wie Sie das abstrahieren möchten, HttpClientum 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 IHttpHandlerund die HttpClientAbstraktion 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 IHttpClientkann dann verwendet werden, um ein reales / konkretes HttpClientoder 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 HttpClientspezifischen 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 HttpClientist, 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();
}
HttpClientin Ihrer Benutzeroberfläche anzeigen , liegt das Problem darin. Sie zwingen Ihren Client, dieHttpClientkonkrete Klasse zu verwenden. Stattdessen sollten Sie eine Abstraktion desHttpClient.