Comments (13)
Parece que estás usando un named client, ¿no? Si el constructor del servicio de Datos Basicos recibe un IHttpClientFactory, puedes configurar un mock que simula tu ejemplo como esto:
var handler = new Mock<HttpMessageHandler>();
var factory = handler.CreateClientFactory();
Mock.Get(factory).Setup(x => x.CreateClient(NamesEndPointConstants.DatosBasicos))
.Returns(() =>
{
var client = handler.CreateClient();
client.BaseAddress = new Uri(UrlDatosBasicos); // Tal vez una constante en la prueba unitaria
return client;
});
var datosBasicos = new DatosBasicosServicio(factory);
// o si usas AutoMocker:
mocker.Use<IHttpClientFactory>(factory);
var datosBasicos = mocker.CreateInstance<DatosBasicosServicio>();
Pero si recibe un HttpClient, puedes simplemente hacer esto:
var handler = new Mock<HttpMessageHandler>();
var client = handler.CreateClient();
client.BaseAddress = new Uri(UrlDatosBasicos);
Porque el retry policy no es un trabajo del métodos de servicio, creo no es necesario aquí. Quieres probar retry, yo escribiría una prueba unitaria para GetRetryPolicy(). Por favor ves aquí para un ejemplo.
Espero que esto ayude. (Mi español no es bueno, lo siento.)
from moq.contrib.httpclient.
Yo quiero usarlo pero en un TestServer. Haciendo pruebas a la web api.
Mi Inyección real en el startup, es esta.
services.AddHttpClient(NamesEndPointConstants.IdentityServer, config =>
{
config.BaseAddress = new Uri(configuration.GetOicAuthority());
}).SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddPolicyHandler(GetRetryPolicy());
Quiero hacer que el Mock para reemplazar esa configuración. Me es útil para que mi sistema no se comunique con sistemas externos.
En la siguiente la clase en factory.ConfigureTestServices(services =>, puede reemplazar inyecciones de dependencia.
Si tienes alguna idea sera muy bueno.
En el momento me toco hacer Mock al servicio externo, pero me gustaria burla al HttpClient y que tener una mayor cobertura de pruebas.
Si puedes ayudarme, excelente.
public class ClaseDescuentoControllerTest : IClassFixture<ApiWebApplicationFactory>
{
private readonly HttpClient _client;
public ClaseDescuentoControllerTest(ApiWebApplicationFactory factory)
{
factory.ConfigureTestServices(services =>
{
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IConsultarTerceroDatosBasicosService));
services.Remove(descriptor);
var mockConsultarTerceroDatosBasicosServiceHttp = Mock.Of<IConsultarTerceroDatosBasicosService>();
Mock.Get(mockConsultarTerceroDatosBasicosServiceHttp)
.Setup(m => m.Consultar(It.IsAny<string>()))
.Returns(new ConsultarTerceroDatosBasicosServiceResponse(new TerceroModelView() { Identificacion = "1003195636" }));
services.AddTransient<IConsultarTerceroDatosBasicosService>(sp => mockConsultarTerceroDatosBasicosServiceHttp);
});
_client = factory.CreateClient();
}
from moq.contrib.httpclient.
¡Ah, es pruebas de integración! Lo siento, ahora entiendo.
Yo envió una muestra de proyecto de ASP.NET Core y de prueba de integración:
test/IntegrationTestExample/Startup.cs
test/IntegrationTestExample.Test/ExampleTests.cs
Creo que quieres algo como esto:
public class ClaseDescuentoControllerTest : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly WebApplicationFactory<Startup> _factory;
private readonly Mock<HttpMessageHandler> _datosBasicosHandler = new();
public ClaseDescuentoControllerTest(WebApplicationFactory<Startup> factory)
{
_factory = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddHttpClient(NamesEndPointConstants.DatosBasicos)
.ConfigurePrimaryHttpMessageHandler(() => _datosBasicosHandler.Object);
});
});
}
// ...
}
from moq.contrib.httpclient.
Buenas noche, no me funciona.
Al parecer si inyecta el mock, pero no funciona correctamente.
One or more errors occurred. (Object reference not set to an instance of an object.) al crear el cliente var resultado = response.Result;// here
Thank you!!
public ConsultarTerceroDatosBasicosServiceResponse Consultar(string identificacion)
{
var apiClient = _factoryHttp.Crear(NamesEndPointConstants.DatosBasicos);
var response = apiClient.Result.GetAsync($"api/Consulta/Tercero/{identificacion}");
var resultado = response.Result;// here
if (!response.Result.IsSuccessStatusCode)
{
return new ConsultarTerceroDatosBasicosServiceResponse(null);
}
else
{
var respuesta = response.Result.Content.ReadAsStringAsync().Result;
var responseAgent = JsonConvert.DeserializeObject<TerceroModelView>(respuesta);
return new ConsultarTerceroDatosBasicosServiceResponse(responseAgent);
}
}
Test
[Fact]
public async Task GetClaseDescuentoRegistroTestAsync()
{
TerceroModelView terceroEntidad = new () { Identificacion = "1003195636" };
// Notice there was no need to set a BaseAddress in this test class. All of the dependency injection and the
// AddHttpClient() in ConfigureServices() are essentially unchanged and working as they would normally.
_datosBasicosHandler.SetupRequest(HttpMethod.Get, $"https://signusfinanciero/datosbasicos/api/Consulta/Tercero/{terceroEntidad.Identificacion}")
.ReturnsResponse(JsonConvert.SerializeObject(terceroEntidad), "application/json");
var client = _factory.CreateClient();
var roles = new List<string>() { "ContabilidadApi_Integracion" };
var token = JwtService.GenerateToken("boris", roles);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
var vigencia = 2020;
var result = await client.GetAsync($"/api/ClaseDescuentoRegistro/DatosFormulario/{vigencia}");
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
}
Config test
factory.ConfigureTestServices(services =>
{
/* REEMPLAZA EL SERVICIO
var mockConsultarTerceroDatosBasicosServiceHttp = Mock.Of<IConsultarTerceroDatosBasicosService>();
Mock.Get(mockConsultarTerceroDatosBasicosServiceHttp)
.Setup(m => m.Consultar(It.IsAny<string>()))
.Returns(new ConsultarTerceroDatosBasicosServiceResponse(new TerceroModelView() { Identificacion = "1003195636" }));
services.AddTransient<IConsultarTerceroDatosBasicosService>(sp => mockConsultarTerceroDatosBasicosServiceHttp);*/
//REEMPLAZA LA PETICION EXTERNA
services.AddHttpClient(NamesEndPointConstants.DatosBasicos)
.ConfigurePrimaryHttpMessageHandler(() => _datosBasicosHandler.Object);
});
from moq.contrib.httpclient.
Si he entendido bien, _factoryHttp.Crear()
es un async wrapper de IHttpClientFactory.CreateClient()
y apiClient
es decididamente un HttpClient que usa el mock handler, ¿no? Pero response
es null.
Primero, .Result
es blocking y propenso a los errores. Si no puedes hacer que Consultar() sea async, consideras:
public ConsultarTerceroDatosBasicosServiceResponse Consultar(string identificacion)
{
return ConsultarAsync(identificacion).Result;
}
public async Task<ConsultarTerceroDatosBasicosServiceResponse> ConsultarAsync(string identificacion)
{
HttpClient apiClient = await _factoryHttp.Crear(NamesEndPointConstants.DatosBasicos);
HttpResponseMessage response = await apiClient.GetAsync($"api/Consulta/Tercero/{identificacion}");
if (!response.IsSuccessStatusCode)
{
return new ConsultarTerceroDatosBasicosServiceResponse(null);
}
string respuesta = await response.Content.ReadAsStringAsync();
var responseAgent = JsonConvert.DeserializeObject<TerceroModelView>(respuesta);
return new ConsultarTerceroDatosBasicosServiceResponse(responseAgent);
}
Esto es más fácil de leer IMO.
Segundo, es posible que la request no está coincidiendo con el setup. Pruebas MockBehavior.Strict por favor... lanzaría un MockException con un mensaje de error más detallado si es así.
-private readonly Mock<HttpMessageHandler> _datosBasicosHandler = new();
+private readonly Mock<HttpMessageHandler> _datosBasicosHandler = new(MockBehavior.Strict);
MockBehavior.Loose (default):
MockBehavior.Strict:
from moq.contrib.httpclient.
Por cierto, espero que no te importe, yo edité tu respuesta porque los code blocks fueron rotos. Para code de multi-línea, utilizas tres backticks con el nombre del idioma, como:
```cs
code
```
from moq.contrib.httpclient.
Gracias por todo., desde Valledupar/Colombia.
Thanks for everything.
The issue was the Headers added by _factoryHttp.Create,
Congratulations on Moq.Contrib.HttpClient.
Maybe an example is missing mocking a request with a successful token.
var urlRequest = $"https://signusfinanciero/datosbasicos/api/Consulta/Tercero/{terceroEntidad.Identificacion}";
_datosBasicosHandler.SetupRequest(HttpMethod.Get, urlRequest, request =>
{
var token=request.Headers.Authorization.Scheme;
var userName = request.Headers.GetValues("ByAUserName").FirstOrDefault();
return token=="TOKEN" && userName=="boris";
})
.ReturnsResponse(JsonConvert.SerializeObject(terceroEntidad), "application/json");
from moq.contrib.httpclient.
Funcionó bien pero otra vez error:
System.AggregateException : One or more errors occurred. (HttpMessageHandler.SendAsync(Method: GET, RequestUri: 'https://signusfinanciero/datosbasicos/api/Consulta/Tercero/1003195636', Version: 1.1, Content: , Headers:
{
Authorization: Bearer Token
ByAUserName: boris
}, CancellationToken) invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.)
---- Moq.MockException : HttpMessageHandler.SendAsync(Method: GET, RequestUri: 'https://signusfinanciero/datosbasicos/api/Consulta/Tercero/1003195636', Version: 1.1, Content: , Headers:
{
Authorization: Bearer Token
ByAUserName: boris
}, CancellationToken) invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
from moq.contrib.httpclient.
Aquí, request.Headers.Authorization.Scheme
es "Bearer" y .Parameter
es "Token". ¿Puedes confirmar que el setup es correcto? Estoy viendo el ejemplo de arriba; es posible que estás comparando Scheme
por accidente. (Y también "TOKEN" vs. "Token".)
from moq.contrib.httpclient.
Ahora sí!!! Correcto. Green!
var urlRequest = $"https://signusfinanciero/datosbasicos/api/Consulta/Tercero/{terceroEntidad.Identificacion}";
_datosBasicosHandler.SetupRequest(HttpMethod.Get, urlRequest, request =>
{
var scheme = request.Headers.Authorization.Scheme;
var parameter = request.Headers.Authorization.Parameter;
var userName = request.Headers.GetValues("ByAUserName").FirstOrDefault();
return scheme == "Bearer" && parameter.ToUpper()=="TOKEN" && userName == "boris";
})
.ReturnsResponse(JsonConvert.SerializeObject(terceroEntidad), "application/json");
from moq.contrib.httpclient.
Te comentó que me tocó suspender el Moq, no me funciona cuando ejecuto todas la pruebas juntas.
Escenario:
El test del que estamos tratando esta dentro de una clase abstracta. Esta clase abstracta la heredó a un clase de prueba para implementar una base de datos en memoria y otra clase prueba para implementar con SqlServer.
Tambien tengo varios tests en esas clases.
Solo ese método de prueba que estamos tratando es quien una el Mock a httpClient, por tanto se ejecuta para InMemory y para SqlServer.
Cuando ejecuto cada test individual funciona bien.
Cuando ejecuto solo los dos test del Mock (inMemory y SqlServer) funciona.
Cuando ejecuto esos dos test junto con otros test no funciona.
El error que presenta es como si los datos que verifica no coincidieran, pero yo depuro y son los correctos. Además observo como si se perdiera la configuración del Mock.
Es algo muy extraño. Por ahora suspendido el uso de tu Nuget ( parece muy bueno pero vimos esas limitantes que no comprendemos) y por ahora continuamos haciendo Mock al servicio que llamaba al HttpClient.
Espero que te sierva la retroalimentación y en el caso que nos puedas dar una sugerencias si es un problema de nosotros.
Gracias por todo el apoyo recibido.
from moq.contrib.httpclient.
Este es el código de lo descrito anteriormente.
namespace Contabilidad.Infrastructure.Web.Test.ClasesDescuento
{
public abstract class BaseClaseDescuentoControllerTest
{
protected string[] roles = new[] { RolesConstants.Integracion };
protected BaseWebApplicationFactory<TestStartup> Factory { get; }
protected ITestOutputHelper Output { get; }
protected readonly Mock<HttpMessageHandler> _datosBasicosHandler = new(MockBehavior.Strict);
protected BaseClaseDescuentoControllerTest(BaseWebApplicationFactory<TestStartup> factory, ITestOutputHelper output)
{
Output = output;
Factory = factory;
Factory.ConfigureTestServices(services =>
{
services.AddHttpClient(NamesEndPointConstants.DatosBasicos)
.ConfigurePrimaryHttpMessageHandler(() => _datosBasicosHandler.Object);
//var mockConsultarTerceroDatosBasicosServiceHttp = Mock.Of<IConsultarTerceroDatosBasicosService>();
//Mock.Get(mockConsultarTerceroDatosBasicosServiceHttp)
//.Setup(m => m.Consultar(It.IsAny<string>()))
//.Returns(new ConsultarTerceroDatosBasicosServiceResponse(new TerceroModelView() { Identificacion = "1003195636" }));
//services.AddTransient<IConsultarTerceroDatosBasicosService>(sp => mockConsultarTerceroDatosBasicosServiceHttp);
});
}
[Fact]
public async Task GetClaseDescuentoTestAsync()
{
var httpClient = Factory.CreateClient().GenerarToken(roles);
var vigencia = 2020;
var result = await httpClient.GetAsync($"/api/ClaseDescuento/Vigencia/{vigencia}");
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
}
[Fact]
public async Task GetClaseDescuentoDatosFormularioTestAsync()
{
var httpClient = Factory.CreateClient().GenerarToken(roles);
var vigencia = 2020;
var result = await httpClient.GetAsync($"/api/ClaseDescuentoRegistro/DatosFormulario/{vigencia}");
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
}
[Fact]
public async Task PostClaseDescuentoRegistroTestAsync()
{
var httpClient = Factory.CreateClient().GenerarToken(roles);
#region UsoEjemploMockPendiente
TerceroModelView terceroEntidad = new() { Identificacion = "1003195636" };
var urlRequest = $"https://signusfinanciero/datosbasicos/api/Consulta/Tercero/{terceroEntidad.Identificacion}";
_datosBasicosHandler.SetupRequest(HttpMethod.Get, urlRequest, request =>
{
var schemeEnviado = request.Headers.Authorization.Scheme.ToLower();
var parameterEnviado = request.Headers.Authorization.Parameter.ToLower();
var userNameEnviado = request.Headers.GetValues("ByAUserName").FirstOrDefault();
var validateResult =
schemeEnviado == "bearer" &&
parameterEnviado == "token" &&
userNameEnviado == username;
return validateResult;
})
.ReturnsResponse(JsonConvert.SerializeObject(terceroEntidad), "application/json");
#endregion
//usar object Mother fluent
var cuentaContableId = 1000;
var request = new RegistrarClaseDescuentoRequest
{
Codigo = "123-121",
ConNit = true,
Nit = "1003195636",
Municipal = true,
IncluirEnAnticipo = true,
Descripcion = "Clase de descuento Test",
PorcentajeDescuento = 4,
PorcentajeValorBase = 2,
Redondeo = 4,
CuentaContableId = cuentaContableId,
Vigencia = 2020,
CodigoTipoValorBase = "VSI"
};
var result = await httpClient.PostAsync("/api/ClaseDescuentoRegistro/", request);
var jsonFromPostResponse = await result.Content.ReadAsStringAsync();
Output.WriteLine(jsonFromPostResponse);
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
}
}
}
namespace Contabilidad.Infrastructure.Web.Test.InMemory.ClasesDescuento
{
using Contabilidad.Infrastructure.Web.Test.ClasesDescuento;
[Collection("DatabaseInMemory")]
[Trait("Category", "Integration")]
public class ClaseDescuentoControllerInMemoryTest :
BaseClaseDescuentoControllerTest, IClassFixture<ApiWebApplicationInMemoryFactory>
{
public ClaseDescuentoControllerInMemoryTest
(ApiWebApplicationInMemoryFactory factory, ITestOutputHelper output) : base(factory, output)
{
}
}
}
namespace Contabilidad.Infrastructure.Web.Test.SqlServer.ClasesDescuento
{
using Contabilidad.Infrastructure.Web.Test.ClasesDescuento;
[Collection("DatabaseSqlServer")]
[Trait("Category", "EndToEnd")]
public class ClaseDescuentoControllerInSqlServerTest :
BaseClaseDescuentoControllerTest, IClassFixture<ApiWebApplicationSqlServerFactory>
{
public ClaseDescuentoControllerInSqlServerTest(ApiWebApplicationSqlServerFactory factory, ITestOutputHelper output) : base(factory, output)
{
}
}
}
namespace Contabilidad.Infrastructure.Web.Test.Postgresql.ClasesDescuento
{
using Contabilidad.Infrastructure.Web.Test.ClasesDescuento;
[Collection("DatabasePostgres")]
[Trait("Category", "EndToEnd")]
public class ClasesDescuentoControllerInPostgresqlTest :
BaseClaseDescuentoControllerTest, IClassFixture<ApiWebApplicationPostgresFactory>
{
public ClasesDescuentoControllerInPostgresqlTest
(ApiWebApplicationPostgresFactory factory, ITestOutputHelper output) : base(factory, output)
{
}
}
}
from moq.contrib.httpclient.
Te dejo el código.
Gracias.
from moq.contrib.httpclient.
Related Issues (9)
- Question: In the handler.VerifyRequest ... HOT 1
- Method for validating request Content HOT 2
- Issues with 'ReturnsJsonResponse' and Deserializing the Response-Content HOT 2
- Compiler warning CS8002: Referenced assembly does not have a strong name HOT 2
- Unsupported expression: x => x.CreateClient() Extension methods (here: HttpClientFactoryExtensions.CreateClient) may not be used in setup / verification expressions. HOT 12
- Repeated calls to SetupRequest only returns reponse once, then fails HOT 3
- How to fake a network error which is represented by an Exception thrown? HOT 3
- Dotnet 7: The value cannot be null or empty. (Parameter 'mediaType') HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from moq.contrib.httpclient.