Estoy escribiendo casos de prueba usando xUnit y Moq.
Estoy intentando simular PostAsync() de HttpClient, pero aparece un error.
A continuación se muestra el código utilizado para la burla:
public TestADLS_Operations() { var mockClient = new Mock<HttpClient>(); mockClient.Setup(repo => repo.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>())).Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); this._iADLS_Operations = new ADLS_Operations(mockClient.Object); }Error:
Expresión no admitida: repo => repo.PostAsync(It.IsAny(), It.IsAny()) Los miembros no reemplazables (aquí: HttpClient.PostAsync) no se pueden usar en expresiones de configuración/verificación.
Captura de pantalla:
Los miembros no reemplazables (aquí: HttpClient.PostAsync) no se pueden usar en expresiones de configuración/verificación.
También traté de burlarme de HttpClient de la misma manera que lo hiciste y recibí el mismo mensaje de error.
Solución:
HttpClient , HttpMessageHandler . Luego, proporcione el mockHttpMessageHandler.Object a su HttpClient , que luego pasará a su clase de código de producto. Esto funciona porque HttpClient usa HttpMessageHandler bajo el capó:
// Arrange var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); mockHttpMessageHandler.Protected() .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()) .ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK }); var client = new HttpClient(mockHttpMessageHandler.Object); this._iADLS_Operations = new ADLS_Operations(client);Nota: También necesitará un
using Moq.Protected;en la parte superior de su archivo de prueba.
Luego puede llamar a su método que usa PostAsync desde su prueba, y PostAsync devolverá una respuesta de estado HTTP OK:
// Act var returnedItem = this._iADLS_Operations.MethodThatUsesPostAsync(/*parameter(s) here*/); Ventaja: HttpMessageHandler significa que no necesita clases adicionales en su código de producto o su código de prueba.
Recursos útiles:
Como explican otras respuestas, debe burlarse de HttpMessageHandler o HttpClientFactory, no de HttpClient. Este es un escenario tan común que alguien creó una biblioteca auxiliar para ambos casos, Moq.Contrib.HttpClient .
Copiando del ejemplo de General Usage para HttpClient :
// All requests made with HttpClient go through its handler's SendAsync() which we mock var handler = new Mock<HttpMessageHandler>(); var client = handler.CreateClient(); // A simple example that returns 404 for any request handler.SetupAnyRequest() .ReturnsResponse(HttpStatusCode.NotFound); // Match GET requests to an endpoint that returns json (defaults to 200 OK) handler.SetupRequest(HttpMethod.Get, "https://example.com/api/stuff") .ReturnsResponse(JsonConvert.SerializeObject(model), "application/json"); // Setting additional headers on the response using the optional configure action handler.SetupRequest("https://example.com/api/stuff") .ReturnsResponse(bytes, configure: response => { response.Content.Headers.LastModified = new DateTime(2018, 3, 9); }) .Verifiable(); // Naturally we can use Moq methods as well // Verify methods are provided matching the setup helpers handler.VerifyAnyRequest(Times.Exactly(3));Para HttpClientFactory:
var handler = new Mock<HttpMessageHandler>(); var factory = handler.CreateClientFactory(); // Named clients can be configured as well (overriding the default) Mock.Get(factory).Setup(x => x.CreateClient("api")) .Returns(() => { var client = handler.CreateClient(); client.BaseAddress = ApiBaseUrl; return client; });En lugar de usar directamente una instancia de HttpClient en su código, use una IHttpClientFactory . En sus pruebas, puede crear su propia implementación de IHttpClientFactory que devuelve un HttpClient que se conecta a un TestServer .
Aquí hay un ejemplo de cómo podría verse su Fake Factory:
public class InMemoryHttpClientFactory: IHttpClientFactory { private readonly TestServer _server; public InMemoryHttpClientFactory(TestServer server) { _server = server; } public HttpClient CreateClient(string name) { return _server.CreateClient(); } }Luego puede configurar un TestServer en sus pruebas y hacer que su IHttpClientFactory personalizado cree clientes para ese servidor:
public TestADLS_Operations() { //setup TestServer IWebHostBuilder hostBuilder = new WebHostBuilder() .Configure(app => app.Run( async context => { // set your response headers via the context.Response.Headers property // set your response content like this: byte[] content = Encoding.Unicode.GetBytes("myResponseContent"); await context.Response.Body.WriteAsync(content); })); var testServer = new TestServer(hostBuilder) var factory = new InMemoryHttpClientFactory(testServer); _iADLS_Operations = new ADLS_Operations(factory); [...] }